diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/fman')
21 files changed, 11903 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig new file mode 100644 index 000000000..48bf80887 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/Kconfig @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only +config FSL_FMAN + tristate "FMan support" + depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST + select GENERIC_ALLOCATOR + select PHYLIB + select CRC32 + default n + help + Freescale Data-Path Acceleration Architecture Frame Manager + (FMan) support + +config DPAA_ERRATUM_A050385 + bool + depends on ARM64 && FSL_DPAA + default y + help + DPAA FMan erratum A050385 software workaround implementation: + align buffers, data start, SG fragment length to avoid FMan DMA + splits. + FMAN DMA read or writes under heavy traffic load may cause FMAN + internal resource leak thus stopping further packet processing. + The FMAN internal queue can overflow when FMAN splits single + read or write transactions into multiple smaller transactions + such that more than 17 AXI transactions are in flight from FMAN + to interconnect. When the FMAN internal queue overflows, it can + stall further packet processing. The issue can occur with any + one of the following three conditions: + 1. FMAN AXI transaction crosses 4K address boundary (Errata + A010022) + 2. FMAN DMA address for an AXI transaction is not 16 byte + aligned, i.e. the last 4 bits of an address are non-zero + 3. Scatter Gather (SG) frames have more than one SG buffer in + the SG list and any one of the buffers, except the last + buffer in the SG list has data size that is not a multiple + of 16 bytes, i.e., other than 16, 32, 48, 64, etc. + With any one of the above three conditions present, there is + likelihood of stalled FMAN packet processing, especially under + stress with multiple ports injecting line-rate traffic. diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile new file mode 100644 index 000000000..b618091db --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman + +obj-$(CONFIG_FSL_FMAN) += fsl_dpaa_fman.o +obj-$(CONFIG_FSL_FMAN) += fsl_dpaa_fman_port.o +obj-$(CONFIG_FSL_FMAN) += fsl_dpaa_mac.o + +fsl_dpaa_fman-objs := fman_muram.o fman.o fman_sp.o fman_keygen.o +fsl_dpaa_fman_port-objs := fman_port.o +fsl_dpaa_mac-objs:= mac.o fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c new file mode 100644 index 000000000..ce0a12158 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -0,0 +1,2961 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * Copyright 2020 NXP + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/fsl/guts.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/clk.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/interrupt.h> +#include <linux/libfdt_env.h> + +#include "fman.h" +#include "fman_muram.h" +#include "fman_keygen.h" + +/* General defines */ +#define FMAN_LIODN_TBL 64 /* size of LIODN table */ +#define MAX_NUM_OF_MACS 10 +#define FM_NUM_OF_FMAN_CTRL_EVENT_REGS 4 +#define BASE_RX_PORTID 0x08 +#define BASE_TX_PORTID 0x28 + +/* Modules registers offsets */ +#define BMI_OFFSET 0x00080000 +#define QMI_OFFSET 0x00080400 +#define KG_OFFSET 0x000C1000 +#define DMA_OFFSET 0x000C2000 +#define FPM_OFFSET 0x000C3000 +#define IMEM_OFFSET 0x000C4000 +#define HWP_OFFSET 0x000C7000 +#define CGP_OFFSET 0x000DB000 + +/* Exceptions bit map */ +#define EX_DMA_BUS_ERROR 0x80000000 +#define EX_DMA_READ_ECC 0x40000000 +#define EX_DMA_SYSTEM_WRITE_ECC 0x20000000 +#define EX_DMA_FM_WRITE_ECC 0x10000000 +#define EX_FPM_STALL_ON_TASKS 0x08000000 +#define EX_FPM_SINGLE_ECC 0x04000000 +#define EX_FPM_DOUBLE_ECC 0x02000000 +#define EX_QMI_SINGLE_ECC 0x01000000 +#define EX_QMI_DEQ_FROM_UNKNOWN_PORTID 0x00800000 +#define EX_QMI_DOUBLE_ECC 0x00400000 +#define EX_BMI_LIST_RAM_ECC 0x00200000 +#define EX_BMI_STORAGE_PROFILE_ECC 0x00100000 +#define EX_BMI_STATISTICS_RAM_ECC 0x00080000 +#define EX_IRAM_ECC 0x00040000 +#define EX_MURAM_ECC 0x00020000 +#define EX_BMI_DISPATCH_RAM_ECC 0x00010000 +#define EX_DMA_SINGLE_PORT_ECC 0x00008000 + +/* DMA defines */ +/* masks */ +#define DMA_MODE_BER 0x00200000 +#define DMA_MODE_ECC 0x00000020 +#define DMA_MODE_SECURE_PROT 0x00000800 +#define DMA_MODE_AXI_DBG_MASK 0x0F000000 + +#define DMA_TRANSFER_PORTID_MASK 0xFF000000 +#define DMA_TRANSFER_TNUM_MASK 0x00FF0000 +#define DMA_TRANSFER_LIODN_MASK 0x00000FFF + +#define DMA_STATUS_BUS_ERR 0x08000000 +#define DMA_STATUS_READ_ECC 0x04000000 +#define DMA_STATUS_SYSTEM_WRITE_ECC 0x02000000 +#define DMA_STATUS_FM_WRITE_ECC 0x01000000 +#define DMA_STATUS_FM_SPDAT_ECC 0x00080000 + +#define DMA_MODE_CACHE_OR_SHIFT 30 +#define DMA_MODE_AXI_DBG_SHIFT 24 +#define DMA_MODE_CEN_SHIFT 13 +#define DMA_MODE_CEN_MASK 0x00000007 +#define DMA_MODE_DBG_SHIFT 7 +#define DMA_MODE_AID_MODE_SHIFT 4 + +#define DMA_THRESH_COMMQ_SHIFT 24 +#define DMA_THRESH_READ_INT_BUF_SHIFT 16 +#define DMA_THRESH_READ_INT_BUF_MASK 0x0000003f +#define DMA_THRESH_WRITE_INT_BUF_MASK 0x0000003f + +#define DMA_TRANSFER_PORTID_SHIFT 24 +#define DMA_TRANSFER_TNUM_SHIFT 16 + +#define DMA_CAM_SIZEOF_ENTRY 0x40 +#define DMA_CAM_UNITS 8 + +#define DMA_LIODN_SHIFT 16 +#define DMA_LIODN_BASE_MASK 0x00000FFF + +/* FPM defines */ +#define FPM_EV_MASK_DOUBLE_ECC 0x80000000 +#define FPM_EV_MASK_STALL 0x40000000 +#define FPM_EV_MASK_SINGLE_ECC 0x20000000 +#define FPM_EV_MASK_RELEASE_FM 0x00010000 +#define FPM_EV_MASK_DOUBLE_ECC_EN 0x00008000 +#define FPM_EV_MASK_STALL_EN 0x00004000 +#define FPM_EV_MASK_SINGLE_ECC_EN 0x00002000 +#define FPM_EV_MASK_EXTERNAL_HALT 0x00000008 +#define FPM_EV_MASK_ECC_ERR_HALT 0x00000004 + +#define FPM_RAM_MURAM_ECC 0x00008000 +#define FPM_RAM_IRAM_ECC 0x00004000 +#define FPM_IRAM_ECC_ERR_EX_EN 0x00020000 +#define FPM_MURAM_ECC_ERR_EX_EN 0x00040000 +#define FPM_RAM_IRAM_ECC_EN 0x40000000 +#define FPM_RAM_RAMS_ECC_EN 0x80000000 +#define FPM_RAM_RAMS_ECC_EN_SRC_SEL 0x08000000 + +#define FPM_REV1_MAJOR_MASK 0x0000FF00 +#define FPM_REV1_MINOR_MASK 0x000000FF + +#define FPM_DISP_LIMIT_SHIFT 24 + +#define FPM_PRT_FM_CTL1 0x00000001 +#define FPM_PRT_FM_CTL2 0x00000002 +#define FPM_PORT_FM_CTL_PORTID_SHIFT 24 +#define FPM_PRC_ORA_FM_CTL_SEL_SHIFT 16 + +#define FPM_THR1_PRS_SHIFT 24 +#define FPM_THR1_KG_SHIFT 16 +#define FPM_THR1_PLCR_SHIFT 8 +#define FPM_THR1_BMI_SHIFT 0 + +#define FPM_THR2_QMI_ENQ_SHIFT 24 +#define FPM_THR2_QMI_DEQ_SHIFT 0 +#define FPM_THR2_FM_CTL1_SHIFT 16 +#define FPM_THR2_FM_CTL2_SHIFT 8 + +#define FPM_EV_MASK_CAT_ERR_SHIFT 1 +#define FPM_EV_MASK_DMA_ERR_SHIFT 0 + +#define FPM_REV1_MAJOR_SHIFT 8 + +#define FPM_RSTC_FM_RESET 0x80000000 +#define FPM_RSTC_MAC0_RESET 0x40000000 +#define FPM_RSTC_MAC1_RESET 0x20000000 +#define FPM_RSTC_MAC2_RESET 0x10000000 +#define FPM_RSTC_MAC3_RESET 0x08000000 +#define FPM_RSTC_MAC8_RESET 0x04000000 +#define FPM_RSTC_MAC4_RESET 0x02000000 +#define FPM_RSTC_MAC5_RESET 0x01000000 +#define FPM_RSTC_MAC6_RESET 0x00800000 +#define FPM_RSTC_MAC7_RESET 0x00400000 +#define FPM_RSTC_MAC9_RESET 0x00200000 + +#define FPM_TS_INT_SHIFT 16 +#define FPM_TS_CTL_EN 0x80000000 + +/* BMI defines */ +#define BMI_INIT_START 0x80000000 +#define BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC 0x80000000 +#define BMI_ERR_INTR_EN_LIST_RAM_ECC 0x40000000 +#define BMI_ERR_INTR_EN_STATISTICS_RAM_ECC 0x20000000 +#define BMI_ERR_INTR_EN_DISPATCH_RAM_ECC 0x10000000 +#define BMI_NUM_OF_TASKS_MASK 0x3F000000 +#define BMI_NUM_OF_EXTRA_TASKS_MASK 0x000F0000 +#define BMI_NUM_OF_DMAS_MASK 0x00000F00 +#define BMI_NUM_OF_EXTRA_DMAS_MASK 0x0000000F +#define BMI_FIFO_SIZE_MASK 0x000003FF +#define BMI_EXTRA_FIFO_SIZE_MASK 0x03FF0000 +#define BMI_CFG2_DMAS_MASK 0x0000003F +#define BMI_CFG2_TASKS_MASK 0x0000003F + +#define BMI_CFG2_TASKS_SHIFT 16 +#define BMI_CFG2_DMAS_SHIFT 0 +#define BMI_CFG1_FIFO_SIZE_SHIFT 16 +#define BMI_NUM_OF_TASKS_SHIFT 24 +#define BMI_EXTRA_NUM_OF_TASKS_SHIFT 16 +#define BMI_NUM_OF_DMAS_SHIFT 8 +#define BMI_EXTRA_NUM_OF_DMAS_SHIFT 0 + +#define BMI_FIFO_ALIGN 0x100 + +#define BMI_EXTRA_FIFO_SIZE_SHIFT 16 + +/* QMI defines */ +#define QMI_CFG_ENQ_EN 0x80000000 +#define QMI_CFG_DEQ_EN 0x40000000 +#define QMI_CFG_EN_COUNTERS 0x10000000 +#define QMI_CFG_DEQ_MASK 0x0000003F +#define QMI_CFG_ENQ_MASK 0x00003F00 +#define QMI_CFG_ENQ_SHIFT 8 + +#define QMI_ERR_INTR_EN_DOUBLE_ECC 0x80000000 +#define QMI_ERR_INTR_EN_DEQ_FROM_DEF 0x40000000 +#define QMI_INTR_EN_SINGLE_ECC 0x80000000 + +#define QMI_GS_HALT_NOT_BUSY 0x00000002 + +/* HWP defines */ +#define HWP_RPIMAC_PEN 0x00000001 + +/* IRAM defines */ +#define IRAM_IADD_AIE 0x80000000 +#define IRAM_READY 0x80000000 + +/* Default values */ +#define DEFAULT_CATASTROPHIC_ERR 0 +#define DEFAULT_DMA_ERR 0 +#define DEFAULT_AID_MODE FMAN_DMA_AID_OUT_TNUM +#define DEFAULT_DMA_COMM_Q_LOW 0x2A +#define DEFAULT_DMA_COMM_Q_HIGH 0x3F +#define DEFAULT_CACHE_OVERRIDE 0 +#define DEFAULT_DMA_CAM_NUM_OF_ENTRIES 64 +#define DEFAULT_DMA_DBG_CNT_MODE 0 +#define DEFAULT_DMA_SOS_EMERGENCY 0 +#define DEFAULT_DMA_WATCHDOG 0 +#define DEFAULT_DISP_LIMIT 0 +#define DEFAULT_PRS_DISP_TH 16 +#define DEFAULT_PLCR_DISP_TH 16 +#define DEFAULT_KG_DISP_TH 16 +#define DEFAULT_BMI_DISP_TH 16 +#define DEFAULT_QMI_ENQ_DISP_TH 16 +#define DEFAULT_QMI_DEQ_DISP_TH 16 +#define DEFAULT_FM_CTL1_DISP_TH 16 +#define DEFAULT_FM_CTL2_DISP_TH 16 + +#define DFLT_AXI_DBG_NUM_OF_BEATS 1 + +#define DFLT_DMA_READ_INT_BUF_LOW(dma_thresh_max_buf) \ + ((dma_thresh_max_buf + 1) / 2) +#define DFLT_DMA_READ_INT_BUF_HIGH(dma_thresh_max_buf) \ + ((dma_thresh_max_buf + 1) * 3 / 4) +#define DFLT_DMA_WRITE_INT_BUF_LOW(dma_thresh_max_buf) \ + ((dma_thresh_max_buf + 1) / 2) +#define DFLT_DMA_WRITE_INT_BUF_HIGH(dma_thresh_max_buf)\ + ((dma_thresh_max_buf + 1) * 3 / 4) + +#define DMA_COMM_Q_LOW_FMAN_V3 0x2A +#define DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq) \ + ((dma_thresh_max_commq + 1) / 2) +#define DFLT_DMA_COMM_Q_LOW(major, dma_thresh_max_commq) \ + ((major == 6) ? DMA_COMM_Q_LOW_FMAN_V3 : \ + DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq)) + +#define DMA_COMM_Q_HIGH_FMAN_V3 0x3f +#define DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq) \ + ((dma_thresh_max_commq + 1) * 3 / 4) +#define DFLT_DMA_COMM_Q_HIGH(major, dma_thresh_max_commq) \ + ((major == 6) ? DMA_COMM_Q_HIGH_FMAN_V3 : \ + DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq)) + +#define TOTAL_NUM_OF_TASKS_FMAN_V3L 59 +#define TOTAL_NUM_OF_TASKS_FMAN_V3H 124 +#define DFLT_TOTAL_NUM_OF_TASKS(major, minor, bmi_max_num_of_tasks) \ + ((major == 6) ? ((minor == 1 || minor == 4) ? \ + TOTAL_NUM_OF_TASKS_FMAN_V3L : TOTAL_NUM_OF_TASKS_FMAN_V3H) : \ + bmi_max_num_of_tasks) + +#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 64 +#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V2 32 +#define DFLT_DMA_CAM_NUM_OF_ENTRIES(major) \ + (major == 6 ? DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 : \ + DMA_CAM_NUM_OF_ENTRIES_FMAN_V2) + +#define FM_TIMESTAMP_1_USEC_BIT 8 + +/* Defines used for enabling/disabling FMan interrupts */ +#define ERR_INTR_EN_DMA 0x00010000 +#define ERR_INTR_EN_FPM 0x80000000 +#define ERR_INTR_EN_BMI 0x00800000 +#define ERR_INTR_EN_QMI 0x00400000 +#define ERR_INTR_EN_MURAM 0x00040000 +#define ERR_INTR_EN_MAC0 0x00004000 +#define ERR_INTR_EN_MAC1 0x00002000 +#define ERR_INTR_EN_MAC2 0x00001000 +#define ERR_INTR_EN_MAC3 0x00000800 +#define ERR_INTR_EN_MAC4 0x00000400 +#define ERR_INTR_EN_MAC5 0x00000200 +#define ERR_INTR_EN_MAC6 0x00000100 +#define ERR_INTR_EN_MAC7 0x00000080 +#define ERR_INTR_EN_MAC8 0x00008000 +#define ERR_INTR_EN_MAC9 0x00000040 + +#define INTR_EN_QMI 0x40000000 +#define INTR_EN_MAC0 0x00080000 +#define INTR_EN_MAC1 0x00040000 +#define INTR_EN_MAC2 0x00020000 +#define INTR_EN_MAC3 0x00010000 +#define INTR_EN_MAC4 0x00000040 +#define INTR_EN_MAC5 0x00000020 +#define INTR_EN_MAC6 0x00000008 +#define INTR_EN_MAC7 0x00000002 +#define INTR_EN_MAC8 0x00200000 +#define INTR_EN_MAC9 0x00100000 +#define INTR_EN_REV0 0x00008000 +#define INTR_EN_REV1 0x00004000 +#define INTR_EN_REV2 0x00002000 +#define INTR_EN_REV3 0x00001000 +#define INTR_EN_TMR 0x01000000 + +enum fman_dma_aid_mode { + FMAN_DMA_AID_OUT_PORT_ID = 0, /* 4 LSB of PORT_ID */ + FMAN_DMA_AID_OUT_TNUM /* 4 LSB of TNUM */ +}; + +struct fman_iram_regs { + u32 iadd; /* FM IRAM instruction address register */ + u32 idata; /* FM IRAM instruction data register */ + u32 itcfg; /* FM IRAM timing config register */ + u32 iready; /* FM IRAM ready register */ +}; + +struct fman_fpm_regs { + u32 fmfp_tnc; /* FPM TNUM Control 0x00 */ + u32 fmfp_prc; /* FPM Port_ID FmCtl Association 0x04 */ + u32 fmfp_brkc; /* FPM Breakpoint Control 0x08 */ + u32 fmfp_mxd; /* FPM Flush Control 0x0c */ + u32 fmfp_dist1; /* FPM Dispatch Thresholds1 0x10 */ + u32 fmfp_dist2; /* FPM Dispatch Thresholds2 0x14 */ + u32 fm_epi; /* FM Error Pending Interrupts 0x18 */ + u32 fm_rie; /* FM Error Interrupt Enable 0x1c */ + u32 fmfp_fcev[4]; /* FPM FMan-Controller Event 1-4 0x20-0x2f */ + u32 res0030[4]; /* res 0x30 - 0x3f */ + u32 fmfp_cee[4]; /* PM FMan-Controller Event 1-4 0x40-0x4f */ + u32 res0050[4]; /* res 0x50-0x5f */ + u32 fmfp_tsc1; /* FPM TimeStamp Control1 0x60 */ + u32 fmfp_tsc2; /* FPM TimeStamp Control2 0x64 */ + u32 fmfp_tsp; /* FPM Time Stamp 0x68 */ + u32 fmfp_tsf; /* FPM Time Stamp Fraction 0x6c */ + u32 fm_rcr; /* FM Rams Control 0x70 */ + u32 fmfp_extc; /* FPM External Requests Control 0x74 */ + u32 fmfp_ext1; /* FPM External Requests Config1 0x78 */ + u32 fmfp_ext2; /* FPM External Requests Config2 0x7c */ + u32 fmfp_drd[16]; /* FPM Data_Ram Data 0-15 0x80 - 0xbf */ + u32 fmfp_dra; /* FPM Data Ram Access 0xc0 */ + u32 fm_ip_rev_1; /* FM IP Block Revision 1 0xc4 */ + u32 fm_ip_rev_2; /* FM IP Block Revision 2 0xc8 */ + u32 fm_rstc; /* FM Reset Command 0xcc */ + u32 fm_cld; /* FM Classifier Debug 0xd0 */ + u32 fm_npi; /* FM Normal Pending Interrupts 0xd4 */ + u32 fmfp_exte; /* FPM External Requests Enable 0xd8 */ + u32 fmfp_ee; /* FPM Event&Mask 0xdc */ + u32 fmfp_cev[4]; /* FPM CPU Event 1-4 0xe0-0xef */ + u32 res00f0[4]; /* res 0xf0-0xff */ + u32 fmfp_ps[50]; /* FPM Port Status 0x100-0x1c7 */ + u32 res01c8[14]; /* res 0x1c8-0x1ff */ + u32 fmfp_clfabc; /* FPM CLFABC 0x200 */ + u32 fmfp_clfcc; /* FPM CLFCC 0x204 */ + u32 fmfp_clfaval; /* FPM CLFAVAL 0x208 */ + u32 fmfp_clfbval; /* FPM CLFBVAL 0x20c */ + u32 fmfp_clfcval; /* FPM CLFCVAL 0x210 */ + u32 fmfp_clfamsk; /* FPM CLFAMSK 0x214 */ + u32 fmfp_clfbmsk; /* FPM CLFBMSK 0x218 */ + u32 fmfp_clfcmsk; /* FPM CLFCMSK 0x21c */ + u32 fmfp_clfamc; /* FPM CLFAMC 0x220 */ + u32 fmfp_clfbmc; /* FPM CLFBMC 0x224 */ + u32 fmfp_clfcmc; /* FPM CLFCMC 0x228 */ + u32 fmfp_decceh; /* FPM DECCEH 0x22c */ + u32 res0230[116]; /* res 0x230 - 0x3ff */ + u32 fmfp_ts[128]; /* 0x400: FPM Task Status 0x400 - 0x5ff */ + u32 res0600[0x400 - 384]; +}; + +struct fman_bmi_regs { + u32 fmbm_init; /* BMI Initialization 0x00 */ + u32 fmbm_cfg1; /* BMI Configuration 1 0x04 */ + u32 fmbm_cfg2; /* BMI Configuration 2 0x08 */ + u32 res000c[5]; /* 0x0c - 0x1f */ + u32 fmbm_ievr; /* Interrupt Event Register 0x20 */ + u32 fmbm_ier; /* Interrupt Enable Register 0x24 */ + u32 fmbm_ifr; /* Interrupt Force Register 0x28 */ + u32 res002c[5]; /* 0x2c - 0x3f */ + u32 fmbm_arb[8]; /* BMI Arbitration 0x40 - 0x5f */ + u32 res0060[12]; /* 0x60 - 0x8f */ + u32 fmbm_dtc[3]; /* Debug Trap Counter 0x90 - 0x9b */ + u32 res009c; /* 0x9c */ + u32 fmbm_dcv[3][4]; /* Debug Compare val 0xa0-0xcf */ + u32 fmbm_dcm[3][4]; /* Debug Compare Mask 0xd0-0xff */ + u32 fmbm_gde; /* BMI Global Debug Enable 0x100 */ + u32 fmbm_pp[63]; /* BMI Port Parameters 0x104 - 0x1ff */ + u32 res0200; /* 0x200 */ + u32 fmbm_pfs[63]; /* BMI Port FIFO Size 0x204 - 0x2ff */ + u32 res0300; /* 0x300 */ + u32 fmbm_spliodn[63]; /* Port Partition ID 0x304 - 0x3ff */ +}; + +struct fman_qmi_regs { + u32 fmqm_gc; /* General Configuration Register 0x00 */ + u32 res0004; /* 0x04 */ + u32 fmqm_eie; /* Error Interrupt Event Register 0x08 */ + u32 fmqm_eien; /* Error Interrupt Enable Register 0x0c */ + u32 fmqm_eif; /* Error Interrupt Force Register 0x10 */ + u32 fmqm_ie; /* Interrupt Event Register 0x14 */ + u32 fmqm_ien; /* Interrupt Enable Register 0x18 */ + u32 fmqm_if; /* Interrupt Force Register 0x1c */ + u32 fmqm_gs; /* Global Status Register 0x20 */ + u32 fmqm_ts; /* Task Status Register 0x24 */ + u32 fmqm_etfc; /* Enqueue Total Frame Counter 0x28 */ + u32 fmqm_dtfc; /* Dequeue Total Frame Counter 0x2c */ + u32 fmqm_dc0; /* Dequeue Counter 0 0x30 */ + u32 fmqm_dc1; /* Dequeue Counter 1 0x34 */ + u32 fmqm_dc2; /* Dequeue Counter 2 0x38 */ + u32 fmqm_dc3; /* Dequeue Counter 3 0x3c */ + u32 fmqm_dfdc; /* Dequeue FQID from Default Counter 0x40 */ + u32 fmqm_dfcc; /* Dequeue FQID from Context Counter 0x44 */ + u32 fmqm_dffc; /* Dequeue FQID from FD Counter 0x48 */ + u32 fmqm_dcc; /* Dequeue Confirm Counter 0x4c */ + u32 res0050[7]; /* 0x50 - 0x6b */ + u32 fmqm_tapc; /* Tnum Aging Period Control 0x6c */ + u32 fmqm_dmcvc; /* Dequeue MAC Command Valid Counter 0x70 */ + u32 fmqm_difdcc; /* Dequeue Invalid FD Command Counter 0x74 */ + u32 fmqm_da1v; /* Dequeue A1 Valid Counter 0x78 */ + u32 res007c; /* 0x7c */ + u32 fmqm_dtc; /* 0x80 Debug Trap Counter 0x80 */ + u32 fmqm_efddd; /* 0x84 Enqueue Frame desc Dynamic dbg 0x84 */ + u32 res0088[2]; /* 0x88 - 0x8f */ + struct { + u32 fmqm_dtcfg1; /* 0x90 dbg trap cfg 1 Register 0x00 */ + u32 fmqm_dtval1; /* Debug Trap Value 1 Register 0x04 */ + u32 fmqm_dtm1; /* Debug Trap Mask 1 Register 0x08 */ + u32 fmqm_dtc1; /* Debug Trap Counter 1 Register 0x0c */ + u32 fmqm_dtcfg2; /* dbg Trap cfg 2 Register 0x10 */ + u32 fmqm_dtval2; /* Debug Trap Value 2 Register 0x14 */ + u32 fmqm_dtm2; /* Debug Trap Mask 2 Register 0x18 */ + u32 res001c; /* 0x1c */ + } dbg_traps[3]; /* 0x90 - 0xef */ + u8 res00f0[0x400 - 0xf0]; /* 0xf0 - 0x3ff */ +}; + +struct fman_dma_regs { + u32 fmdmsr; /* FM DMA status register 0x00 */ + u32 fmdmmr; /* FM DMA mode register 0x04 */ + u32 fmdmtr; /* FM DMA bus threshold register 0x08 */ + u32 fmdmhy; /* FM DMA bus hysteresis register 0x0c */ + u32 fmdmsetr; /* FM DMA SOS emergency Threshold Register 0x10 */ + u32 fmdmtah; /* FM DMA transfer bus address high reg 0x14 */ + u32 fmdmtal; /* FM DMA transfer bus address low reg 0x18 */ + u32 fmdmtcid; /* FM DMA transfer bus communication ID reg 0x1c */ + u32 fmdmra; /* FM DMA bus internal ram address register 0x20 */ + u32 fmdmrd; /* FM DMA bus internal ram data register 0x24 */ + u32 fmdmwcr; /* FM DMA CAM watchdog counter value 0x28 */ + u32 fmdmebcr; /* FM DMA CAM base in MURAM register 0x2c */ + u32 fmdmccqdr; /* FM DMA CAM and CMD Queue Debug reg 0x30 */ + u32 fmdmccqvr1; /* FM DMA CAM and CMD Queue Value reg #1 0x34 */ + u32 fmdmccqvr2; /* FM DMA CAM and CMD Queue Value reg #2 0x38 */ + u32 fmdmcqvr3; /* FM DMA CMD Queue Value register #3 0x3c */ + u32 fmdmcqvr4; /* FM DMA CMD Queue Value register #4 0x40 */ + u32 fmdmcqvr5; /* FM DMA CMD Queue Value register #5 0x44 */ + u32 fmdmsefrc; /* FM DMA Semaphore Entry Full Reject Cntr 0x48 */ + u32 fmdmsqfrc; /* FM DMA Semaphore Queue Full Reject Cntr 0x4c */ + u32 fmdmssrc; /* FM DMA Semaphore SYNC Reject Counter 0x50 */ + u32 fmdmdcr; /* FM DMA Debug Counter 0x54 */ + u32 fmdmemsr; /* FM DMA Emergency Smoother Register 0x58 */ + u32 res005c; /* 0x5c */ + u32 fmdmplr[FMAN_LIODN_TBL / 2]; /* DMA LIODN regs 0x60-0xdf */ + u32 res00e0[0x400 - 56]; +}; + +struct fman_hwp_regs { + u32 res0000[0x844 / 4]; /* 0x000..0x843 */ + u32 fmprrpimac; /* FM Parser Internal memory access control */ + u32 res[(0x1000 - 0x848) / 4]; /* 0x848..0xFFF */ +}; + +/* Structure that holds current FMan state. + * Used for saving run time information. + */ +struct fman_state_struct { + u8 fm_id; + u16 fm_clk_freq; + struct fman_rev_info rev_info; + bool enabled_time_stamp; + u8 count1_micro_bit; + u8 total_num_of_tasks; + u8 accumulated_num_of_tasks; + u32 accumulated_fifo_size; + u8 accumulated_num_of_open_dmas; + u8 accumulated_num_of_deq_tnums; + u32 exceptions; + u32 extra_fifo_pool_size; + u8 extra_tasks_pool_size; + u8 extra_open_dmas_pool_size; + u16 port_mfl[MAX_NUM_OF_MACS]; + u16 mac_mfl[MAX_NUM_OF_MACS]; + + /* SOC specific */ + u32 fm_iram_size; + /* DMA */ + u32 dma_thresh_max_commq; + u32 dma_thresh_max_buf; + u32 max_num_of_open_dmas; + /* QMI */ + u32 qmi_max_num_of_tnums; + u32 qmi_def_tnums_thresh; + /* BMI */ + u32 bmi_max_num_of_tasks; + u32 bmi_max_fifo_size; + /* General */ + u32 fm_port_num_of_cg; + u32 num_of_rx_ports; + u32 total_fifo_size; + + u32 qman_channel_base; + u32 num_of_qman_channels; + + struct resource *res; +}; + +/* Structure that holds FMan initial configuration */ +struct fman_cfg { + u8 disp_limit_tsh; + u8 prs_disp_tsh; + u8 plcr_disp_tsh; + u8 kg_disp_tsh; + u8 bmi_disp_tsh; + u8 qmi_enq_disp_tsh; + u8 qmi_deq_disp_tsh; + u8 fm_ctl1_disp_tsh; + u8 fm_ctl2_disp_tsh; + int dma_cache_override; + enum fman_dma_aid_mode dma_aid_mode; + u32 dma_axi_dbg_num_of_beats; + u32 dma_cam_num_of_entries; + u32 dma_watchdog; + u8 dma_comm_qtsh_asrt_emer; + u32 dma_write_buf_tsh_asrt_emer; + u32 dma_read_buf_tsh_asrt_emer; + u8 dma_comm_qtsh_clr_emer; + u32 dma_write_buf_tsh_clr_emer; + u32 dma_read_buf_tsh_clr_emer; + u32 dma_sos_emergency; + int dma_dbg_cnt_mode; + int catastrophic_err; + int dma_err; + u32 exceptions; + u16 clk_freq; + u32 cam_base_addr; + u32 fifo_base_addr; + u32 total_fifo_size; + u32 total_num_of_tasks; + u32 qmi_def_tnums_thresh; +}; + +#ifdef CONFIG_DPAA_ERRATUM_A050385 +static bool fman_has_err_a050385; +#endif + +static irqreturn_t fman_exceptions(struct fman *fman, + enum fman_exceptions exception) +{ + dev_dbg(fman->dev, "%s: FMan[%d] exception %d\n", + __func__, fman->state->fm_id, exception); + + return IRQ_HANDLED; +} + +static irqreturn_t fman_bus_error(struct fman *fman, u8 __maybe_unused port_id, + u64 __maybe_unused addr, + u8 __maybe_unused tnum, + u16 __maybe_unused liodn) +{ + dev_dbg(fman->dev, "%s: FMan[%d] bus error: port_id[%d]\n", + __func__, fman->state->fm_id, port_id); + + return IRQ_HANDLED; +} + +static inline irqreturn_t call_mac_isr(struct fman *fman, u8 id) +{ + if (fman->intr_mng[id].isr_cb) { + fman->intr_mng[id].isr_cb(fman->intr_mng[id].src_handle); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static inline u8 hw_port_id_to_sw_port_id(u8 major, u8 hw_port_id) +{ + u8 sw_port_id = 0; + + if (hw_port_id >= BASE_TX_PORTID) + sw_port_id = hw_port_id - BASE_TX_PORTID; + else if (hw_port_id >= BASE_RX_PORTID) + sw_port_id = hw_port_id - BASE_RX_PORTID; + else + sw_port_id = 0; + + return sw_port_id; +} + +static void set_port_order_restoration(struct fman_fpm_regs __iomem *fpm_rg, + u8 port_id) +{ + u32 tmp = 0; + + tmp = port_id << FPM_PORT_FM_CTL_PORTID_SHIFT; + + tmp |= FPM_PRT_FM_CTL2 | FPM_PRT_FM_CTL1; + + /* order restoration */ + if (port_id % 2) + tmp |= FPM_PRT_FM_CTL1 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT; + else + tmp |= FPM_PRT_FM_CTL2 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT; + + iowrite32be(tmp, &fpm_rg->fmfp_prc); +} + +static void set_port_liodn(struct fman *fman, u8 port_id, + u32 liodn_base, u32 liodn_ofst) +{ + u32 tmp; + + iowrite32be(liodn_ofst, &fman->bmi_regs->fmbm_spliodn[port_id - 1]); + if (!IS_ENABLED(CONFIG_FSL_PAMU)) + return; + /* set LIODN base for this port */ + tmp = ioread32be(&fman->dma_regs->fmdmplr[port_id / 2]); + if (port_id % 2) { + tmp &= ~DMA_LIODN_BASE_MASK; + tmp |= liodn_base; + } else { + tmp &= ~(DMA_LIODN_BASE_MASK << DMA_LIODN_SHIFT); + tmp |= liodn_base << DMA_LIODN_SHIFT; + } + iowrite32be(tmp, &fman->dma_regs->fmdmplr[port_id / 2]); +} + +static void enable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg) +{ + u32 tmp; + + tmp = ioread32be(&fpm_rg->fm_rcr); + if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL) + iowrite32be(tmp | FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr); + else + iowrite32be(tmp | FPM_RAM_RAMS_ECC_EN | + FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr); +} + +static void disable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg) +{ + u32 tmp; + + tmp = ioread32be(&fpm_rg->fm_rcr); + if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL) + iowrite32be(tmp & ~FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr); + else + iowrite32be(tmp & ~(FPM_RAM_RAMS_ECC_EN | FPM_RAM_IRAM_ECC_EN), + &fpm_rg->fm_rcr); +} + +static void fman_defconfig(struct fman_cfg *cfg) +{ + memset(cfg, 0, sizeof(struct fman_cfg)); + + cfg->catastrophic_err = DEFAULT_CATASTROPHIC_ERR; + cfg->dma_err = DEFAULT_DMA_ERR; + cfg->dma_aid_mode = DEFAULT_AID_MODE; + cfg->dma_comm_qtsh_clr_emer = DEFAULT_DMA_COMM_Q_LOW; + cfg->dma_comm_qtsh_asrt_emer = DEFAULT_DMA_COMM_Q_HIGH; + cfg->dma_cache_override = DEFAULT_CACHE_OVERRIDE; + cfg->dma_cam_num_of_entries = DEFAULT_DMA_CAM_NUM_OF_ENTRIES; + cfg->dma_dbg_cnt_mode = DEFAULT_DMA_DBG_CNT_MODE; + cfg->dma_sos_emergency = DEFAULT_DMA_SOS_EMERGENCY; + cfg->dma_watchdog = DEFAULT_DMA_WATCHDOG; + cfg->disp_limit_tsh = DEFAULT_DISP_LIMIT; + cfg->prs_disp_tsh = DEFAULT_PRS_DISP_TH; + cfg->plcr_disp_tsh = DEFAULT_PLCR_DISP_TH; + cfg->kg_disp_tsh = DEFAULT_KG_DISP_TH; + cfg->bmi_disp_tsh = DEFAULT_BMI_DISP_TH; + cfg->qmi_enq_disp_tsh = DEFAULT_QMI_ENQ_DISP_TH; + cfg->qmi_deq_disp_tsh = DEFAULT_QMI_DEQ_DISP_TH; + cfg->fm_ctl1_disp_tsh = DEFAULT_FM_CTL1_DISP_TH; + cfg->fm_ctl2_disp_tsh = DEFAULT_FM_CTL2_DISP_TH; +} + +static int dma_init(struct fman *fman) +{ + struct fman_dma_regs __iomem *dma_rg = fman->dma_regs; + struct fman_cfg *cfg = fman->cfg; + u32 tmp_reg; + + /* Init DMA Registers */ + + /* clear status reg events */ + tmp_reg = (DMA_STATUS_BUS_ERR | DMA_STATUS_READ_ECC | + DMA_STATUS_SYSTEM_WRITE_ECC | DMA_STATUS_FM_WRITE_ECC); + iowrite32be(ioread32be(&dma_rg->fmdmsr) | tmp_reg, &dma_rg->fmdmsr); + + /* configure mode register */ + tmp_reg = 0; + tmp_reg |= cfg->dma_cache_override << DMA_MODE_CACHE_OR_SHIFT; + if (cfg->exceptions & EX_DMA_BUS_ERROR) + tmp_reg |= DMA_MODE_BER; + if ((cfg->exceptions & EX_DMA_SYSTEM_WRITE_ECC) | + (cfg->exceptions & EX_DMA_READ_ECC) | + (cfg->exceptions & EX_DMA_FM_WRITE_ECC)) + tmp_reg |= DMA_MODE_ECC; + if (cfg->dma_axi_dbg_num_of_beats) + tmp_reg |= (DMA_MODE_AXI_DBG_MASK & + ((cfg->dma_axi_dbg_num_of_beats - 1) + << DMA_MODE_AXI_DBG_SHIFT)); + + tmp_reg |= (((cfg->dma_cam_num_of_entries / DMA_CAM_UNITS) - 1) & + DMA_MODE_CEN_MASK) << DMA_MODE_CEN_SHIFT; + tmp_reg |= DMA_MODE_SECURE_PROT; + tmp_reg |= cfg->dma_dbg_cnt_mode << DMA_MODE_DBG_SHIFT; + tmp_reg |= cfg->dma_aid_mode << DMA_MODE_AID_MODE_SHIFT; + + iowrite32be(tmp_reg, &dma_rg->fmdmmr); + + /* configure thresholds register */ + tmp_reg = ((u32)cfg->dma_comm_qtsh_asrt_emer << + DMA_THRESH_COMMQ_SHIFT); + tmp_reg |= (cfg->dma_read_buf_tsh_asrt_emer & + DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT; + tmp_reg |= cfg->dma_write_buf_tsh_asrt_emer & + DMA_THRESH_WRITE_INT_BUF_MASK; + + iowrite32be(tmp_reg, &dma_rg->fmdmtr); + + /* configure hysteresis register */ + tmp_reg = ((u32)cfg->dma_comm_qtsh_clr_emer << + DMA_THRESH_COMMQ_SHIFT); + tmp_reg |= (cfg->dma_read_buf_tsh_clr_emer & + DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT; + tmp_reg |= cfg->dma_write_buf_tsh_clr_emer & + DMA_THRESH_WRITE_INT_BUF_MASK; + + iowrite32be(tmp_reg, &dma_rg->fmdmhy); + + /* configure emergency threshold */ + iowrite32be(cfg->dma_sos_emergency, &dma_rg->fmdmsetr); + + /* configure Watchdog */ + iowrite32be((cfg->dma_watchdog * cfg->clk_freq), &dma_rg->fmdmwcr); + + iowrite32be(cfg->cam_base_addr, &dma_rg->fmdmebcr); + + /* Allocate MURAM for CAM */ + fman->cam_size = + (u32)(fman->cfg->dma_cam_num_of_entries * DMA_CAM_SIZEOF_ENTRY); + fman->cam_offset = fman_muram_alloc(fman->muram, fman->cam_size); + if (IS_ERR_VALUE(fman->cam_offset)) { + dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n", + __func__); + return -ENOMEM; + } + + if (fman->state->rev_info.major == 2) { + u32 __iomem *cam_base_addr; + + fman_muram_free_mem(fman->muram, fman->cam_offset, + fman->cam_size); + + fman->cam_size = fman->cfg->dma_cam_num_of_entries * 72 + 128; + fman->cam_offset = fman_muram_alloc(fman->muram, + fman->cam_size); + if (IS_ERR_VALUE(fman->cam_offset)) { + dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n", + __func__); + return -ENOMEM; + } + + if (fman->cfg->dma_cam_num_of_entries % 8 || + fman->cfg->dma_cam_num_of_entries > 32) { + dev_err(fman->dev, "%s: wrong dma_cam_num_of_entries\n", + __func__); + return -EINVAL; + } + + cam_base_addr = (u32 __iomem *) + fman_muram_offset_to_vbase(fman->muram, + fman->cam_offset); + iowrite32be(~((1 << + (32 - fman->cfg->dma_cam_num_of_entries)) - 1), + cam_base_addr); + } + + fman->cfg->cam_base_addr = fman->cam_offset; + + return 0; +} + +static void fpm_init(struct fman_fpm_regs __iomem *fpm_rg, struct fman_cfg *cfg) +{ + u32 tmp_reg; + int i; + + /* Init FPM Registers */ + + tmp_reg = (u32)(cfg->disp_limit_tsh << FPM_DISP_LIMIT_SHIFT); + iowrite32be(tmp_reg, &fpm_rg->fmfp_mxd); + + tmp_reg = (((u32)cfg->prs_disp_tsh << FPM_THR1_PRS_SHIFT) | + ((u32)cfg->kg_disp_tsh << FPM_THR1_KG_SHIFT) | + ((u32)cfg->plcr_disp_tsh << FPM_THR1_PLCR_SHIFT) | + ((u32)cfg->bmi_disp_tsh << FPM_THR1_BMI_SHIFT)); + iowrite32be(tmp_reg, &fpm_rg->fmfp_dist1); + + tmp_reg = + (((u32)cfg->qmi_enq_disp_tsh << FPM_THR2_QMI_ENQ_SHIFT) | + ((u32)cfg->qmi_deq_disp_tsh << FPM_THR2_QMI_DEQ_SHIFT) | + ((u32)cfg->fm_ctl1_disp_tsh << FPM_THR2_FM_CTL1_SHIFT) | + ((u32)cfg->fm_ctl2_disp_tsh << FPM_THR2_FM_CTL2_SHIFT)); + iowrite32be(tmp_reg, &fpm_rg->fmfp_dist2); + + /* define exceptions and error behavior */ + tmp_reg = 0; + /* Clear events */ + tmp_reg |= (FPM_EV_MASK_STALL | FPM_EV_MASK_DOUBLE_ECC | + FPM_EV_MASK_SINGLE_ECC); + /* enable interrupts */ + if (cfg->exceptions & EX_FPM_STALL_ON_TASKS) + tmp_reg |= FPM_EV_MASK_STALL_EN; + if (cfg->exceptions & EX_FPM_SINGLE_ECC) + tmp_reg |= FPM_EV_MASK_SINGLE_ECC_EN; + if (cfg->exceptions & EX_FPM_DOUBLE_ECC) + tmp_reg |= FPM_EV_MASK_DOUBLE_ECC_EN; + tmp_reg |= (cfg->catastrophic_err << FPM_EV_MASK_CAT_ERR_SHIFT); + tmp_reg |= (cfg->dma_err << FPM_EV_MASK_DMA_ERR_SHIFT); + /* FMan is not halted upon external halt activation */ + tmp_reg |= FPM_EV_MASK_EXTERNAL_HALT; + /* Man is not halted upon Unrecoverable ECC error behavior */ + tmp_reg |= FPM_EV_MASK_ECC_ERR_HALT; + iowrite32be(tmp_reg, &fpm_rg->fmfp_ee); + + /* clear all fmCtls event registers */ + for (i = 0; i < FM_NUM_OF_FMAN_CTRL_EVENT_REGS; i++) + iowrite32be(0xFFFFFFFF, &fpm_rg->fmfp_cev[i]); + + /* RAM ECC - enable and clear events */ + /* first we need to clear all parser memory, + * as it is uninitialized and may cause ECC errors + */ + /* event bits */ + tmp_reg = (FPM_RAM_MURAM_ECC | FPM_RAM_IRAM_ECC); + + iowrite32be(tmp_reg, &fpm_rg->fm_rcr); + + tmp_reg = 0; + if (cfg->exceptions & EX_IRAM_ECC) { + tmp_reg |= FPM_IRAM_ECC_ERR_EX_EN; + enable_rams_ecc(fpm_rg); + } + if (cfg->exceptions & EX_MURAM_ECC) { + tmp_reg |= FPM_MURAM_ECC_ERR_EX_EN; + enable_rams_ecc(fpm_rg); + } + iowrite32be(tmp_reg, &fpm_rg->fm_rie); +} + +static void bmi_init(struct fman_bmi_regs __iomem *bmi_rg, + struct fman_cfg *cfg) +{ + u32 tmp_reg; + + /* Init BMI Registers */ + + /* define common resources */ + tmp_reg = cfg->fifo_base_addr; + tmp_reg = tmp_reg / BMI_FIFO_ALIGN; + + tmp_reg |= ((cfg->total_fifo_size / FMAN_BMI_FIFO_UNITS - 1) << + BMI_CFG1_FIFO_SIZE_SHIFT); + iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg1); + + tmp_reg = ((cfg->total_num_of_tasks - 1) & BMI_CFG2_TASKS_MASK) << + BMI_CFG2_TASKS_SHIFT; + /* num of DMA's will be dynamically updated when each port is set */ + iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg2); + + /* define unmaskable exceptions, enable and clear events */ + tmp_reg = 0; + iowrite32be(BMI_ERR_INTR_EN_LIST_RAM_ECC | + BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC | + BMI_ERR_INTR_EN_STATISTICS_RAM_ECC | + BMI_ERR_INTR_EN_DISPATCH_RAM_ECC, &bmi_rg->fmbm_ievr); + + if (cfg->exceptions & EX_BMI_LIST_RAM_ECC) + tmp_reg |= BMI_ERR_INTR_EN_LIST_RAM_ECC; + if (cfg->exceptions & EX_BMI_STORAGE_PROFILE_ECC) + tmp_reg |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC; + if (cfg->exceptions & EX_BMI_STATISTICS_RAM_ECC) + tmp_reg |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC; + if (cfg->exceptions & EX_BMI_DISPATCH_RAM_ECC) + tmp_reg |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC; + iowrite32be(tmp_reg, &bmi_rg->fmbm_ier); +} + +static void qmi_init(struct fman_qmi_regs __iomem *qmi_rg, + struct fman_cfg *cfg) +{ + u32 tmp_reg; + + /* Init QMI Registers */ + + /* Clear error interrupt events */ + + iowrite32be(QMI_ERR_INTR_EN_DOUBLE_ECC | QMI_ERR_INTR_EN_DEQ_FROM_DEF, + &qmi_rg->fmqm_eie); + tmp_reg = 0; + if (cfg->exceptions & EX_QMI_DEQ_FROM_UNKNOWN_PORTID) + tmp_reg |= QMI_ERR_INTR_EN_DEQ_FROM_DEF; + if (cfg->exceptions & EX_QMI_DOUBLE_ECC) + tmp_reg |= QMI_ERR_INTR_EN_DOUBLE_ECC; + /* enable events */ + iowrite32be(tmp_reg, &qmi_rg->fmqm_eien); + + tmp_reg = 0; + /* Clear interrupt events */ + iowrite32be(QMI_INTR_EN_SINGLE_ECC, &qmi_rg->fmqm_ie); + if (cfg->exceptions & EX_QMI_SINGLE_ECC) + tmp_reg |= QMI_INTR_EN_SINGLE_ECC; + /* enable events */ + iowrite32be(tmp_reg, &qmi_rg->fmqm_ien); +} + +static void hwp_init(struct fman_hwp_regs __iomem *hwp_rg) +{ + /* enable HW Parser */ + iowrite32be(HWP_RPIMAC_PEN, &hwp_rg->fmprrpimac); +} + +static int enable(struct fman *fman, struct fman_cfg *cfg) +{ + u32 cfg_reg = 0; + + /* Enable all modules */ + + /* clear&enable global counters - calculate reg and save for later, + * because it's the same reg for QMI enable + */ + cfg_reg = QMI_CFG_EN_COUNTERS; + + /* Set enqueue and dequeue thresholds */ + cfg_reg |= (cfg->qmi_def_tnums_thresh << 8) | cfg->qmi_def_tnums_thresh; + + iowrite32be(BMI_INIT_START, &fman->bmi_regs->fmbm_init); + iowrite32be(cfg_reg | QMI_CFG_ENQ_EN | QMI_CFG_DEQ_EN, + &fman->qmi_regs->fmqm_gc); + + return 0; +} + +static int set_exception(struct fman *fman, + enum fman_exceptions exception, bool enable) +{ + u32 tmp; + + switch (exception) { + case FMAN_EX_DMA_BUS_ERROR: + tmp = ioread32be(&fman->dma_regs->fmdmmr); + if (enable) + tmp |= DMA_MODE_BER; + else + tmp &= ~DMA_MODE_BER; + /* disable bus error */ + iowrite32be(tmp, &fman->dma_regs->fmdmmr); + break; + case FMAN_EX_DMA_READ_ECC: + case FMAN_EX_DMA_SYSTEM_WRITE_ECC: + case FMAN_EX_DMA_FM_WRITE_ECC: + tmp = ioread32be(&fman->dma_regs->fmdmmr); + if (enable) + tmp |= DMA_MODE_ECC; + else + tmp &= ~DMA_MODE_ECC; + iowrite32be(tmp, &fman->dma_regs->fmdmmr); + break; + case FMAN_EX_FPM_STALL_ON_TASKS: + tmp = ioread32be(&fman->fpm_regs->fmfp_ee); + if (enable) + tmp |= FPM_EV_MASK_STALL_EN; + else + tmp &= ~FPM_EV_MASK_STALL_EN; + iowrite32be(tmp, &fman->fpm_regs->fmfp_ee); + break; + case FMAN_EX_FPM_SINGLE_ECC: + tmp = ioread32be(&fman->fpm_regs->fmfp_ee); + if (enable) + tmp |= FPM_EV_MASK_SINGLE_ECC_EN; + else + tmp &= ~FPM_EV_MASK_SINGLE_ECC_EN; + iowrite32be(tmp, &fman->fpm_regs->fmfp_ee); + break; + case FMAN_EX_FPM_DOUBLE_ECC: + tmp = ioread32be(&fman->fpm_regs->fmfp_ee); + if (enable) + tmp |= FPM_EV_MASK_DOUBLE_ECC_EN; + else + tmp &= ~FPM_EV_MASK_DOUBLE_ECC_EN; + iowrite32be(tmp, &fman->fpm_regs->fmfp_ee); + break; + case FMAN_EX_QMI_SINGLE_ECC: + tmp = ioread32be(&fman->qmi_regs->fmqm_ien); + if (enable) + tmp |= QMI_INTR_EN_SINGLE_ECC; + else + tmp &= ~QMI_INTR_EN_SINGLE_ECC; + iowrite32be(tmp, &fman->qmi_regs->fmqm_ien); + break; + case FMAN_EX_QMI_DOUBLE_ECC: + tmp = ioread32be(&fman->qmi_regs->fmqm_eien); + if (enable) + tmp |= QMI_ERR_INTR_EN_DOUBLE_ECC; + else + tmp &= ~QMI_ERR_INTR_EN_DOUBLE_ECC; + iowrite32be(tmp, &fman->qmi_regs->fmqm_eien); + break; + case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID: + tmp = ioread32be(&fman->qmi_regs->fmqm_eien); + if (enable) + tmp |= QMI_ERR_INTR_EN_DEQ_FROM_DEF; + else + tmp &= ~QMI_ERR_INTR_EN_DEQ_FROM_DEF; + iowrite32be(tmp, &fman->qmi_regs->fmqm_eien); + break; + case FMAN_EX_BMI_LIST_RAM_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_LIST_RAM_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_LIST_RAM_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_BMI_STORAGE_PROFILE_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_BMI_STATISTICS_RAM_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_STATISTICS_RAM_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_BMI_DISPATCH_RAM_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_DISPATCH_RAM_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_IRAM_ECC: + tmp = ioread32be(&fman->fpm_regs->fm_rie); + if (enable) { + /* enable ECC if not enabled */ + enable_rams_ecc(fman->fpm_regs); + /* enable ECC interrupts */ + tmp |= FPM_IRAM_ECC_ERR_EX_EN; + } else { + /* ECC mechanism may be disabled, + * depending on driver status + */ + disable_rams_ecc(fman->fpm_regs); + tmp &= ~FPM_IRAM_ECC_ERR_EX_EN; + } + iowrite32be(tmp, &fman->fpm_regs->fm_rie); + break; + case FMAN_EX_MURAM_ECC: + tmp = ioread32be(&fman->fpm_regs->fm_rie); + if (enable) { + /* enable ECC if not enabled */ + enable_rams_ecc(fman->fpm_regs); + /* enable ECC interrupts */ + tmp |= FPM_MURAM_ECC_ERR_EX_EN; + } else { + /* ECC mechanism may be disabled, + * depending on driver status + */ + disable_rams_ecc(fman->fpm_regs); + tmp &= ~FPM_MURAM_ECC_ERR_EX_EN; + } + iowrite32be(tmp, &fman->fpm_regs->fm_rie); + break; + default: + return -EINVAL; + } + return 0; +} + +static void resume(struct fman_fpm_regs __iomem *fpm_rg) +{ + u32 tmp; + + tmp = ioread32be(&fpm_rg->fmfp_ee); + /* clear tmp_reg event bits in order not to clear standing events */ + tmp &= ~(FPM_EV_MASK_DOUBLE_ECC | + FPM_EV_MASK_STALL | FPM_EV_MASK_SINGLE_ECC); + tmp |= FPM_EV_MASK_RELEASE_FM; + + iowrite32be(tmp, &fpm_rg->fmfp_ee); +} + +static int fill_soc_specific_params(struct fman_state_struct *state) +{ + u8 minor = state->rev_info.minor; + /* P4080 - Major 2 + * P2041/P3041/P5020/P5040 - Major 3 + * Tx/Bx - Major 6 + */ + switch (state->rev_info.major) { + case 3: + state->bmi_max_fifo_size = 160 * 1024; + state->fm_iram_size = 64 * 1024; + state->dma_thresh_max_commq = 31; + state->dma_thresh_max_buf = 127; + state->qmi_max_num_of_tnums = 64; + state->qmi_def_tnums_thresh = 48; + state->bmi_max_num_of_tasks = 128; + state->max_num_of_open_dmas = 32; + state->fm_port_num_of_cg = 256; + state->num_of_rx_ports = 6; + state->total_fifo_size = 136 * 1024; + break; + + case 2: + state->bmi_max_fifo_size = 160 * 1024; + state->fm_iram_size = 64 * 1024; + state->dma_thresh_max_commq = 31; + state->dma_thresh_max_buf = 127; + state->qmi_max_num_of_tnums = 64; + state->qmi_def_tnums_thresh = 48; + state->bmi_max_num_of_tasks = 128; + state->max_num_of_open_dmas = 32; + state->fm_port_num_of_cg = 256; + state->num_of_rx_ports = 5; + state->total_fifo_size = 100 * 1024; + break; + + case 6: + state->dma_thresh_max_commq = 83; + state->dma_thresh_max_buf = 127; + state->qmi_max_num_of_tnums = 64; + state->qmi_def_tnums_thresh = 32; + state->fm_port_num_of_cg = 256; + + /* FManV3L */ + if (minor == 1 || minor == 4) { + state->bmi_max_fifo_size = 192 * 1024; + state->bmi_max_num_of_tasks = 64; + state->max_num_of_open_dmas = 32; + state->num_of_rx_ports = 5; + if (minor == 1) + state->fm_iram_size = 32 * 1024; + else + state->fm_iram_size = 64 * 1024; + state->total_fifo_size = 156 * 1024; + } + /* FManV3H */ + else if (minor == 0 || minor == 2 || minor == 3) { + state->bmi_max_fifo_size = 384 * 1024; + state->fm_iram_size = 64 * 1024; + state->bmi_max_num_of_tasks = 128; + state->max_num_of_open_dmas = 84; + state->num_of_rx_ports = 8; + state->total_fifo_size = 295 * 1024; + } else { + pr_err("Unsupported FManv3 version\n"); + return -EINVAL; + } + + break; + default: + pr_err("Unsupported FMan version\n"); + return -EINVAL; + } + + return 0; +} + +static bool is_init_done(struct fman_cfg *cfg) +{ + /* Checks if FMan driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +static void free_init_resources(struct fman *fman) +{ + if (fman->cam_offset) + fman_muram_free_mem(fman->muram, fman->cam_offset, + fman->cam_size); + if (fman->fifo_offset) + fman_muram_free_mem(fman->muram, fman->fifo_offset, + fman->fifo_size); +} + +static irqreturn_t bmi_err_event(struct fman *fman) +{ + u32 event, mask, force; + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&bmi_rg->fmbm_ievr); + mask = ioread32be(&bmi_rg->fmbm_ier); + event &= mask; + /* clear the forced events */ + force = ioread32be(&bmi_rg->fmbm_ifr); + if (force & event) + iowrite32be(force & ~event, &bmi_rg->fmbm_ifr); + /* clear the acknowledged events */ + iowrite32be(event, &bmi_rg->fmbm_ievr); + + if (event & BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC); + if (event & BMI_ERR_INTR_EN_LIST_RAM_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_LIST_RAM_ECC); + if (event & BMI_ERR_INTR_EN_STATISTICS_RAM_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC); + if (event & BMI_ERR_INTR_EN_DISPATCH_RAM_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC); + + return ret; +} + +static irqreturn_t qmi_err_event(struct fman *fman) +{ + u32 event, mask, force; + struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&qmi_rg->fmqm_eie); + mask = ioread32be(&qmi_rg->fmqm_eien); + event &= mask; + + /* clear the forced events */ + force = ioread32be(&qmi_rg->fmqm_eif); + if (force & event) + iowrite32be(force & ~event, &qmi_rg->fmqm_eif); + /* clear the acknowledged events */ + iowrite32be(event, &qmi_rg->fmqm_eie); + + if (event & QMI_ERR_INTR_EN_DOUBLE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_QMI_DOUBLE_ECC); + if (event & QMI_ERR_INTR_EN_DEQ_FROM_DEF) + ret = fman->exception_cb(fman, + FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID); + + return ret; +} + +static irqreturn_t dma_err_event(struct fman *fman) +{ + u32 status, mask, com_id; + u8 tnum, port_id, relative_port_id; + u16 liodn; + struct fman_dma_regs __iomem *dma_rg = fman->dma_regs; + irqreturn_t ret = IRQ_NONE; + + status = ioread32be(&dma_rg->fmdmsr); + mask = ioread32be(&dma_rg->fmdmmr); + + /* clear DMA_STATUS_BUS_ERR if mask has no DMA_MODE_BER */ + if ((mask & DMA_MODE_BER) != DMA_MODE_BER) + status &= ~DMA_STATUS_BUS_ERR; + + /* clear relevant bits if mask has no DMA_MODE_ECC */ + if ((mask & DMA_MODE_ECC) != DMA_MODE_ECC) + status &= ~(DMA_STATUS_FM_SPDAT_ECC | + DMA_STATUS_READ_ECC | + DMA_STATUS_SYSTEM_WRITE_ECC | + DMA_STATUS_FM_WRITE_ECC); + + /* clear set events */ + iowrite32be(status, &dma_rg->fmdmsr); + + if (status & DMA_STATUS_BUS_ERR) { + u64 addr; + + addr = (u64)ioread32be(&dma_rg->fmdmtal); + addr |= ((u64)(ioread32be(&dma_rg->fmdmtah)) << 32); + + com_id = ioread32be(&dma_rg->fmdmtcid); + port_id = (u8)(((com_id & DMA_TRANSFER_PORTID_MASK) >> + DMA_TRANSFER_PORTID_SHIFT)); + relative_port_id = + hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id); + tnum = (u8)((com_id & DMA_TRANSFER_TNUM_MASK) >> + DMA_TRANSFER_TNUM_SHIFT); + liodn = (u16)(com_id & DMA_TRANSFER_LIODN_MASK); + ret = fman->bus_error_cb(fman, relative_port_id, addr, tnum, + liodn); + } + if (status & DMA_STATUS_FM_SPDAT_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_SINGLE_PORT_ECC); + if (status & DMA_STATUS_READ_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_READ_ECC); + if (status & DMA_STATUS_SYSTEM_WRITE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC); + if (status & DMA_STATUS_FM_WRITE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_FM_WRITE_ECC); + + return ret; +} + +static irqreturn_t fpm_err_event(struct fman *fman) +{ + u32 event; + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&fpm_rg->fmfp_ee); + /* clear the all occurred events */ + iowrite32be(event, &fpm_rg->fmfp_ee); + + if ((event & FPM_EV_MASK_DOUBLE_ECC) && + (event & FPM_EV_MASK_DOUBLE_ECC_EN)) + ret = fman->exception_cb(fman, FMAN_EX_FPM_DOUBLE_ECC); + if ((event & FPM_EV_MASK_STALL) && (event & FPM_EV_MASK_STALL_EN)) + ret = fman->exception_cb(fman, FMAN_EX_FPM_STALL_ON_TASKS); + if ((event & FPM_EV_MASK_SINGLE_ECC) && + (event & FPM_EV_MASK_SINGLE_ECC_EN)) + ret = fman->exception_cb(fman, FMAN_EX_FPM_SINGLE_ECC); + + return ret; +} + +static irqreturn_t muram_err_intr(struct fman *fman) +{ + u32 event, mask; + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&fpm_rg->fm_rcr); + mask = ioread32be(&fpm_rg->fm_rie); + + /* clear MURAM event bit (do not clear IRAM event) */ + iowrite32be(event & ~FPM_RAM_IRAM_ECC, &fpm_rg->fm_rcr); + + if ((mask & FPM_MURAM_ECC_ERR_EX_EN) && (event & FPM_RAM_MURAM_ECC)) + ret = fman->exception_cb(fman, FMAN_EX_MURAM_ECC); + + return ret; +} + +static irqreturn_t qmi_event(struct fman *fman) +{ + u32 event, mask, force; + struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&qmi_rg->fmqm_ie); + mask = ioread32be(&qmi_rg->fmqm_ien); + event &= mask; + /* clear the forced events */ + force = ioread32be(&qmi_rg->fmqm_if); + if (force & event) + iowrite32be(force & ~event, &qmi_rg->fmqm_if); + /* clear the acknowledged events */ + iowrite32be(event, &qmi_rg->fmqm_ie); + + if (event & QMI_INTR_EN_SINGLE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_QMI_SINGLE_ECC); + + return ret; +} + +static void enable_time_stamp(struct fman *fman) +{ + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + u16 fm_clk_freq = fman->state->fm_clk_freq; + u32 tmp, intgr, ts_freq, frac; + + ts_freq = (u32)(1 << fman->state->count1_micro_bit); + /* configure timestamp so that bit 8 will count 1 microsecond + * Find effective count rate at TIMESTAMP least significant bits: + * Effective_Count_Rate = 1MHz x 2^8 = 256MHz + * Find frequency ratio between effective count rate and the clock: + * Effective_Count_Rate / CLK e.g. for 600 MHz clock: + * 256/600 = 0.4266666... + */ + + intgr = ts_freq / fm_clk_freq; + /* we multiply by 2^16 to keep the fraction of the division + * we do not div back, since we write this value as a fraction + * see spec + */ + + frac = ((ts_freq << 16) - (intgr << 16) * fm_clk_freq) / fm_clk_freq; + /* we check remainder of the division in order to round up if not int */ + if (((ts_freq << 16) - (intgr << 16) * fm_clk_freq) % fm_clk_freq) + frac++; + + tmp = (intgr << FPM_TS_INT_SHIFT) | (u16)frac; + iowrite32be(tmp, &fpm_rg->fmfp_tsc2); + + /* enable timestamp with original clock */ + iowrite32be(FPM_TS_CTL_EN, &fpm_rg->fmfp_tsc1); + fman->state->enabled_time_stamp = true; +} + +static int clear_iram(struct fman *fman) +{ + struct fman_iram_regs __iomem *iram; + int i, count; + + iram = fman->base_addr + IMEM_OFFSET; + + /* Enable the auto-increment */ + iowrite32be(IRAM_IADD_AIE, &iram->iadd); + count = 100; + do { + udelay(1); + } while ((ioread32be(&iram->iadd) != IRAM_IADD_AIE) && --count); + if (count == 0) + return -EBUSY; + + for (i = 0; i < (fman->state->fm_iram_size / 4); i++) + iowrite32be(0xffffffff, &iram->idata); + + iowrite32be(fman->state->fm_iram_size - 4, &iram->iadd); + count = 100; + do { + udelay(1); + } while ((ioread32be(&iram->idata) != 0xffffffff) && --count); + if (count == 0) + return -EBUSY; + + return 0; +} + +static u32 get_exception_flag(enum fman_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FMAN_EX_DMA_BUS_ERROR: + bit_mask = EX_DMA_BUS_ERROR; + break; + case FMAN_EX_DMA_SINGLE_PORT_ECC: + bit_mask = EX_DMA_SINGLE_PORT_ECC; + break; + case FMAN_EX_DMA_READ_ECC: + bit_mask = EX_DMA_READ_ECC; + break; + case FMAN_EX_DMA_SYSTEM_WRITE_ECC: + bit_mask = EX_DMA_SYSTEM_WRITE_ECC; + break; + case FMAN_EX_DMA_FM_WRITE_ECC: + bit_mask = EX_DMA_FM_WRITE_ECC; + break; + case FMAN_EX_FPM_STALL_ON_TASKS: + bit_mask = EX_FPM_STALL_ON_TASKS; + break; + case FMAN_EX_FPM_SINGLE_ECC: + bit_mask = EX_FPM_SINGLE_ECC; + break; + case FMAN_EX_FPM_DOUBLE_ECC: + bit_mask = EX_FPM_DOUBLE_ECC; + break; + case FMAN_EX_QMI_SINGLE_ECC: + bit_mask = EX_QMI_SINGLE_ECC; + break; + case FMAN_EX_QMI_DOUBLE_ECC: + bit_mask = EX_QMI_DOUBLE_ECC; + break; + case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID: + bit_mask = EX_QMI_DEQ_FROM_UNKNOWN_PORTID; + break; + case FMAN_EX_BMI_LIST_RAM_ECC: + bit_mask = EX_BMI_LIST_RAM_ECC; + break; + case FMAN_EX_BMI_STORAGE_PROFILE_ECC: + bit_mask = EX_BMI_STORAGE_PROFILE_ECC; + break; + case FMAN_EX_BMI_STATISTICS_RAM_ECC: + bit_mask = EX_BMI_STATISTICS_RAM_ECC; + break; + case FMAN_EX_BMI_DISPATCH_RAM_ECC: + bit_mask = EX_BMI_DISPATCH_RAM_ECC; + break; + case FMAN_EX_MURAM_ECC: + bit_mask = EX_MURAM_ECC; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static int get_module_event(enum fman_event_modules module, u8 mod_id, + enum fman_intr_type intr_type) +{ + int event; + + switch (module) { + case FMAN_MOD_MAC: + if (intr_type == FMAN_INTR_TYPE_ERR) + event = FMAN_EV_ERR_MAC0 + mod_id; + else + event = FMAN_EV_MAC0 + mod_id; + break; + case FMAN_MOD_FMAN_CTRL: + if (intr_type == FMAN_INTR_TYPE_ERR) + event = FMAN_EV_CNT; + else + event = (FMAN_EV_FMAN_CTRL_0 + mod_id); + break; + case FMAN_MOD_DUMMY_LAST: + event = FMAN_EV_CNT; + break; + default: + event = FMAN_EV_CNT; + break; + } + + return event; +} + +static int set_size_of_fifo(struct fman *fman, u8 port_id, u32 *size_of_fifo, + u32 *extra_size_of_fifo) +{ + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + u32 fifo = *size_of_fifo; + u32 extra_fifo = *extra_size_of_fifo; + u32 tmp; + + /* if this is the first time a port requires extra_fifo_pool_size, + * the total extra_fifo_pool_size must be initialized to 1 buffer per + * port + */ + if (extra_fifo && !fman->state->extra_fifo_pool_size) + fman->state->extra_fifo_pool_size = + fman->state->num_of_rx_ports * FMAN_BMI_FIFO_UNITS; + + fman->state->extra_fifo_pool_size = + max(fman->state->extra_fifo_pool_size, extra_fifo); + + /* check that there are enough uncommitted fifo size */ + if ((fman->state->accumulated_fifo_size + fifo) > + (fman->state->total_fifo_size - + fman->state->extra_fifo_pool_size)) { + dev_err(fman->dev, "%s: Requested fifo size and extra size exceed total FIFO size.\n", + __func__); + return -EAGAIN; + } + + /* Read, modify and write to HW */ + tmp = (fifo / FMAN_BMI_FIFO_UNITS - 1) | + ((extra_fifo / FMAN_BMI_FIFO_UNITS) << + BMI_EXTRA_FIFO_SIZE_SHIFT); + iowrite32be(tmp, &bmi_rg->fmbm_pfs[port_id - 1]); + + /* update accumulated */ + fman->state->accumulated_fifo_size += fifo; + + return 0; +} + +static int set_num_of_tasks(struct fman *fman, u8 port_id, u8 *num_of_tasks, + u8 *num_of_extra_tasks) +{ + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + u8 tasks = *num_of_tasks; + u8 extra_tasks = *num_of_extra_tasks; + u32 tmp; + + if (extra_tasks) + fman->state->extra_tasks_pool_size = + max(fman->state->extra_tasks_pool_size, extra_tasks); + + /* check that there are enough uncommitted tasks */ + if ((fman->state->accumulated_num_of_tasks + tasks) > + (fman->state->total_num_of_tasks - + fman->state->extra_tasks_pool_size)) { + dev_err(fman->dev, "%s: Requested num_of_tasks and extra tasks pool for fm%d exceed total num_of_tasks.\n", + __func__, fman->state->fm_id); + return -EAGAIN; + } + /* update accumulated */ + fman->state->accumulated_num_of_tasks += tasks; + + /* Write to HW */ + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) & + ~(BMI_NUM_OF_TASKS_MASK | BMI_NUM_OF_EXTRA_TASKS_MASK); + tmp |= ((u32)((tasks - 1) << BMI_NUM_OF_TASKS_SHIFT) | + (u32)(extra_tasks << BMI_EXTRA_NUM_OF_TASKS_SHIFT)); + iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]); + + return 0; +} + +static int set_num_of_open_dmas(struct fman *fman, u8 port_id, + u8 *num_of_open_dmas, + u8 *num_of_extra_open_dmas) +{ + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + u8 open_dmas = *num_of_open_dmas; + u8 extra_open_dmas = *num_of_extra_open_dmas; + u8 total_num_dmas = 0, current_val = 0, current_extra_val = 0; + u32 tmp; + + if (!open_dmas) { + /* Configuration according to values in the HW. + * read the current number of open Dma's + */ + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]); + current_extra_val = (u8)((tmp & BMI_NUM_OF_EXTRA_DMAS_MASK) >> + BMI_EXTRA_NUM_OF_DMAS_SHIFT); + + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]); + current_val = (u8)(((tmp & BMI_NUM_OF_DMAS_MASK) >> + BMI_NUM_OF_DMAS_SHIFT) + 1); + + /* This is the first configuration and user did not + * specify value (!open_dmas), reset values will be used + * and we just save these values for resource management + */ + fman->state->extra_open_dmas_pool_size = + (u8)max(fman->state->extra_open_dmas_pool_size, + current_extra_val); + fman->state->accumulated_num_of_open_dmas += current_val; + *num_of_open_dmas = current_val; + *num_of_extra_open_dmas = current_extra_val; + return 0; + } + + if (extra_open_dmas > current_extra_val) + fman->state->extra_open_dmas_pool_size = + (u8)max(fman->state->extra_open_dmas_pool_size, + extra_open_dmas); + + if ((fman->state->rev_info.major < 6) && + (fman->state->accumulated_num_of_open_dmas - current_val + + open_dmas > fman->state->max_num_of_open_dmas)) { + dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds total num_of_open_dmas.\n", + __func__, fman->state->fm_id); + return -EAGAIN; + } else if ((fman->state->rev_info.major >= 6) && + !((fman->state->rev_info.major == 6) && + (fman->state->rev_info.minor == 0)) && + (fman->state->accumulated_num_of_open_dmas - + current_val + open_dmas > + fman->state->dma_thresh_max_commq + 1)) { + dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds DMA Command queue (%d)\n", + __func__, fman->state->fm_id, + fman->state->dma_thresh_max_commq + 1); + return -EAGAIN; + } + + WARN_ON(fman->state->accumulated_num_of_open_dmas < current_val); + /* update acummulated */ + fman->state->accumulated_num_of_open_dmas -= current_val; + fman->state->accumulated_num_of_open_dmas += open_dmas; + + if (fman->state->rev_info.major < 6) + total_num_dmas = + (u8)(fman->state->accumulated_num_of_open_dmas + + fman->state->extra_open_dmas_pool_size); + + /* calculate reg */ + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) & + ~(BMI_NUM_OF_DMAS_MASK | BMI_NUM_OF_EXTRA_DMAS_MASK); + tmp |= (u32)(((open_dmas - 1) << BMI_NUM_OF_DMAS_SHIFT) | + (extra_open_dmas << BMI_EXTRA_NUM_OF_DMAS_SHIFT)); + iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]); + + /* update total num of DMA's with committed number of open DMAS, + * and max uncommitted pool. + */ + if (total_num_dmas) { + tmp = ioread32be(&bmi_rg->fmbm_cfg2) & ~BMI_CFG2_DMAS_MASK; + tmp |= (u32)(total_num_dmas - 1) << BMI_CFG2_DMAS_SHIFT; + iowrite32be(tmp, &bmi_rg->fmbm_cfg2); + } + + return 0; +} + +static int fman_config(struct fman *fman) +{ + void __iomem *base_addr; + int err; + + base_addr = fman->dts_params.base_addr; + + fman->state = kzalloc(sizeof(*fman->state), GFP_KERNEL); + if (!fman->state) + goto err_fm_state; + + /* Allocate the FM driver's parameters structure */ + fman->cfg = kzalloc(sizeof(*fman->cfg), GFP_KERNEL); + if (!fman->cfg) + goto err_fm_drv; + + /* Initialize MURAM block */ + fman->muram = + fman_muram_init(fman->dts_params.muram_res.start, + resource_size(&fman->dts_params.muram_res)); + if (!fman->muram) + goto err_fm_soc_specific; + + /* Initialize FM parameters which will be kept by the driver */ + fman->state->fm_id = fman->dts_params.id; + fman->state->fm_clk_freq = fman->dts_params.clk_freq; + fman->state->qman_channel_base = fman->dts_params.qman_channel_base; + fman->state->num_of_qman_channels = + fman->dts_params.num_of_qman_channels; + fman->state->res = fman->dts_params.res; + fman->exception_cb = fman_exceptions; + fman->bus_error_cb = fman_bus_error; + fman->fpm_regs = base_addr + FPM_OFFSET; + fman->bmi_regs = base_addr + BMI_OFFSET; + fman->qmi_regs = base_addr + QMI_OFFSET; + fman->dma_regs = base_addr + DMA_OFFSET; + fman->hwp_regs = base_addr + HWP_OFFSET; + fman->kg_regs = base_addr + KG_OFFSET; + fman->base_addr = base_addr; + + spin_lock_init(&fman->spinlock); + fman_defconfig(fman->cfg); + + fman->state->extra_fifo_pool_size = 0; + fman->state->exceptions = (EX_DMA_BUS_ERROR | + EX_DMA_READ_ECC | + EX_DMA_SYSTEM_WRITE_ECC | + EX_DMA_FM_WRITE_ECC | + EX_FPM_STALL_ON_TASKS | + EX_FPM_SINGLE_ECC | + EX_FPM_DOUBLE_ECC | + EX_QMI_DEQ_FROM_UNKNOWN_PORTID | + EX_BMI_LIST_RAM_ECC | + EX_BMI_STORAGE_PROFILE_ECC | + EX_BMI_STATISTICS_RAM_ECC | + EX_MURAM_ECC | + EX_BMI_DISPATCH_RAM_ECC | + EX_QMI_DOUBLE_ECC | + EX_QMI_SINGLE_ECC); + + /* Read FMan revision for future use*/ + fman_get_revision(fman, &fman->state->rev_info); + + err = fill_soc_specific_params(fman->state); + if (err) + goto err_fm_soc_specific; + + /* FM_AID_MODE_NO_TNUM_SW005 Errata workaround */ + if (fman->state->rev_info.major >= 6) + fman->cfg->dma_aid_mode = FMAN_DMA_AID_OUT_PORT_ID; + + fman->cfg->qmi_def_tnums_thresh = fman->state->qmi_def_tnums_thresh; + + fman->state->total_num_of_tasks = + (u8)DFLT_TOTAL_NUM_OF_TASKS(fman->state->rev_info.major, + fman->state->rev_info.minor, + fman->state->bmi_max_num_of_tasks); + + if (fman->state->rev_info.major < 6) { + fman->cfg->dma_comm_qtsh_clr_emer = + (u8)DFLT_DMA_COMM_Q_LOW(fman->state->rev_info.major, + fman->state->dma_thresh_max_commq); + + fman->cfg->dma_comm_qtsh_asrt_emer = + (u8)DFLT_DMA_COMM_Q_HIGH(fman->state->rev_info.major, + fman->state->dma_thresh_max_commq); + + fman->cfg->dma_cam_num_of_entries = + DFLT_DMA_CAM_NUM_OF_ENTRIES(fman->state->rev_info.major); + + fman->cfg->dma_read_buf_tsh_clr_emer = + DFLT_DMA_READ_INT_BUF_LOW(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_read_buf_tsh_asrt_emer = + DFLT_DMA_READ_INT_BUF_HIGH(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_write_buf_tsh_clr_emer = + DFLT_DMA_WRITE_INT_BUF_LOW(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_write_buf_tsh_asrt_emer = + DFLT_DMA_WRITE_INT_BUF_HIGH(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_axi_dbg_num_of_beats = + DFLT_AXI_DBG_NUM_OF_BEATS; + } + + return 0; + +err_fm_soc_specific: + kfree(fman->cfg); +err_fm_drv: + kfree(fman->state); +err_fm_state: + kfree(fman); + return -EINVAL; +} + +static int fman_reset(struct fman *fman) +{ + u32 count; + int err = 0; + + if (fman->state->rev_info.major < 6) { + iowrite32be(FPM_RSTC_FM_RESET, &fman->fpm_regs->fm_rstc); + /* Wait for reset completion */ + count = 100; + do { + udelay(1); + } while (((ioread32be(&fman->fpm_regs->fm_rstc)) & + FPM_RSTC_FM_RESET) && --count); + if (count == 0) + err = -EBUSY; + + goto _return; + } else { +#ifdef CONFIG_PPC + struct device_node *guts_node; + struct ccsr_guts __iomem *guts_regs; + u32 devdisr2, reg; + + /* Errata A007273 */ + guts_node = + of_find_compatible_node(NULL, NULL, + "fsl,qoriq-device-config-2.0"); + if (!guts_node) { + dev_err(fman->dev, "%s: Couldn't find guts node\n", + __func__); + goto guts_node; + } + + guts_regs = of_iomap(guts_node, 0); + if (!guts_regs) { + dev_err(fman->dev, "%s: Couldn't map %pOF regs\n", + __func__, guts_node); + goto guts_regs; + } +#define FMAN1_ALL_MACS_MASK 0xFCC00000 +#define FMAN2_ALL_MACS_MASK 0x000FCC00 + /* Read current state */ + devdisr2 = ioread32be(&guts_regs->devdisr2); + if (fman->dts_params.id == 0) + reg = devdisr2 & ~FMAN1_ALL_MACS_MASK; + else + reg = devdisr2 & ~FMAN2_ALL_MACS_MASK; + + /* Enable all MACs */ + iowrite32be(reg, &guts_regs->devdisr2); +#endif + + /* Perform FMan reset */ + iowrite32be(FPM_RSTC_FM_RESET, &fman->fpm_regs->fm_rstc); + + /* Wait for reset completion */ + count = 100; + do { + udelay(1); + } while (((ioread32be(&fman->fpm_regs->fm_rstc)) & + FPM_RSTC_FM_RESET) && --count); + if (count == 0) { +#ifdef CONFIG_PPC + iounmap(guts_regs); + of_node_put(guts_node); +#endif + err = -EBUSY; + goto _return; + } +#ifdef CONFIG_PPC + + /* Restore devdisr2 value */ + iowrite32be(devdisr2, &guts_regs->devdisr2); + + iounmap(guts_regs); + of_node_put(guts_node); +#endif + + goto _return; + +#ifdef CONFIG_PPC +guts_regs: + of_node_put(guts_node); +guts_node: + dev_dbg(fman->dev, "%s: Didn't perform FManV3 reset due to Errata A007273!\n", + __func__); +#endif + } +_return: + return err; +} + +static int fman_init(struct fman *fman) +{ + struct fman_cfg *cfg = NULL; + int err = 0, i, count; + + if (is_init_done(fman->cfg)) + return -EINVAL; + + fman->state->count1_micro_bit = FM_TIMESTAMP_1_USEC_BIT; + + cfg = fman->cfg; + + /* clear revision-dependent non existing exception */ + if (fman->state->rev_info.major < 6) + fman->state->exceptions &= ~FMAN_EX_BMI_DISPATCH_RAM_ECC; + + if (fman->state->rev_info.major >= 6) + fman->state->exceptions &= ~FMAN_EX_QMI_SINGLE_ECC; + + /* clear CPG */ + memset_io((void __iomem *)(fman->base_addr + CGP_OFFSET), 0, + fman->state->fm_port_num_of_cg); + + /* Save LIODN info before FMan reset + * Skipping non-existent port 0 (i = 1) + */ + for (i = 1; i < FMAN_LIODN_TBL; i++) { + u32 liodn_base; + + fman->liodn_offset[i] = + ioread32be(&fman->bmi_regs->fmbm_spliodn[i - 1]); + if (!IS_ENABLED(CONFIG_FSL_PAMU)) + continue; + liodn_base = ioread32be(&fman->dma_regs->fmdmplr[i / 2]); + if (i % 2) { + /* FMDM_PLR LSB holds LIODN base for odd ports */ + liodn_base &= DMA_LIODN_BASE_MASK; + } else { + /* FMDM_PLR MSB holds LIODN base for even ports */ + liodn_base >>= DMA_LIODN_SHIFT; + liodn_base &= DMA_LIODN_BASE_MASK; + } + fman->liodn_base[i] = liodn_base; + } + + err = fman_reset(fman); + if (err) + return err; + + if (ioread32be(&fman->qmi_regs->fmqm_gs) & QMI_GS_HALT_NOT_BUSY) { + resume(fman->fpm_regs); + /* Wait until QMI is not in halt not busy state */ + count = 100; + do { + udelay(1); + } while (((ioread32be(&fman->qmi_regs->fmqm_gs)) & + QMI_GS_HALT_NOT_BUSY) && --count); + if (count == 0) + dev_warn(fman->dev, "%s: QMI is in halt not busy state\n", + __func__); + } + + if (clear_iram(fman) != 0) + return -EINVAL; + + cfg->exceptions = fman->state->exceptions; + + /* Init DMA Registers */ + + err = dma_init(fman); + if (err != 0) { + free_init_resources(fman); + return err; + } + + /* Init FPM Registers */ + fpm_init(fman->fpm_regs, fman->cfg); + + /* define common resources */ + /* allocate MURAM for FIFO according to total size */ + fman->fifo_offset = fman_muram_alloc(fman->muram, + fman->state->total_fifo_size); + if (IS_ERR_VALUE(fman->fifo_offset)) { + free_init_resources(fman); + dev_err(fman->dev, "%s: MURAM alloc for BMI FIFO failed\n", + __func__); + return -ENOMEM; + } + + cfg->fifo_base_addr = fman->fifo_offset; + cfg->total_fifo_size = fman->state->total_fifo_size; + cfg->total_num_of_tasks = fman->state->total_num_of_tasks; + cfg->clk_freq = fman->state->fm_clk_freq; + + /* Init BMI Registers */ + bmi_init(fman->bmi_regs, fman->cfg); + + /* Init QMI Registers */ + qmi_init(fman->qmi_regs, fman->cfg); + + /* Init HW Parser */ + hwp_init(fman->hwp_regs); + + /* Init KeyGen */ + fman->keygen = keygen_init(fman->kg_regs); + if (!fman->keygen) + return -EINVAL; + + err = enable(fman, cfg); + if (err != 0) + return err; + + enable_time_stamp(fman); + + kfree(fman->cfg); + fman->cfg = NULL; + + return 0; +} + +static int fman_set_exception(struct fman *fman, + enum fman_exceptions exception, bool enable) +{ + u32 bit_mask = 0; + + if (!is_init_done(fman->cfg)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + fman->state->exceptions |= bit_mask; + else + fman->state->exceptions &= ~bit_mask; + } else { + dev_err(fman->dev, "%s: Undefined exception (%d)\n", + __func__, exception); + return -EINVAL; + } + + return set_exception(fman, exception, enable); +} + +/** + * fman_register_intr + * @fman: A Pointer to FMan device + * @module: Calling module + * @mod_id: Module id (if more than 1 exists, '0' if not) + * @intr_type: Interrupt type (error/normal) selection. + * @isr_cb: The interrupt service routine. + * @src_arg: Argument to be passed to isr_cb. + * + * Used to register an event handler to be processed by FMan + * + * Return: 0 on success; Error code otherwise. + */ +void fman_register_intr(struct fman *fman, enum fman_event_modules module, + u8 mod_id, enum fman_intr_type intr_type, + void (*isr_cb)(void *src_arg), void *src_arg) +{ + int event = 0; + + event = get_module_event(module, mod_id, intr_type); + WARN_ON(event >= FMAN_EV_CNT); + + /* register in local FM structure */ + fman->intr_mng[event].isr_cb = isr_cb; + fman->intr_mng[event].src_handle = src_arg; +} +EXPORT_SYMBOL(fman_register_intr); + +/** + * fman_unregister_intr + * @fman: A Pointer to FMan device + * @module: Calling module + * @mod_id: Module id (if more than 1 exists, '0' if not) + * @intr_type: Interrupt type (error/normal) selection. + * + * Used to unregister an event handler to be processed by FMan + * + * Return: 0 on success; Error code otherwise. + */ +void fman_unregister_intr(struct fman *fman, enum fman_event_modules module, + u8 mod_id, enum fman_intr_type intr_type) +{ + int event = 0; + + event = get_module_event(module, mod_id, intr_type); + WARN_ON(event >= FMAN_EV_CNT); + + fman->intr_mng[event].isr_cb = NULL; + fman->intr_mng[event].src_handle = NULL; +} +EXPORT_SYMBOL(fman_unregister_intr); + +/** + * fman_set_port_params + * @fman: A Pointer to FMan device + * @port_params: Port parameters + * + * Used by FMan Port to pass parameters to the FMan + * + * Return: 0 on success; Error code otherwise. + */ +int fman_set_port_params(struct fman *fman, + struct fman_port_init_params *port_params) +{ + int err; + unsigned long flags; + u8 port_id = port_params->port_id, mac_id; + + spin_lock_irqsave(&fman->spinlock, flags); + + err = set_num_of_tasks(fman, port_params->port_id, + &port_params->num_of_tasks, + &port_params->num_of_extra_tasks); + if (err) + goto return_err; + + /* TX Ports */ + if (port_params->port_type != FMAN_PORT_TYPE_RX) { + u32 enq_th, deq_th, reg; + + /* update qmi ENQ/DEQ threshold */ + fman->state->accumulated_num_of_deq_tnums += + port_params->deq_pipeline_depth; + enq_th = (ioread32be(&fman->qmi_regs->fmqm_gc) & + QMI_CFG_ENQ_MASK) >> QMI_CFG_ENQ_SHIFT; + /* if enq_th is too big, we reduce it to the max value + * that is still 0 + */ + if (enq_th >= (fman->state->qmi_max_num_of_tnums - + fman->state->accumulated_num_of_deq_tnums)) { + enq_th = + fman->state->qmi_max_num_of_tnums - + fman->state->accumulated_num_of_deq_tnums - 1; + + reg = ioread32be(&fman->qmi_regs->fmqm_gc); + reg &= ~QMI_CFG_ENQ_MASK; + reg |= (enq_th << QMI_CFG_ENQ_SHIFT); + iowrite32be(reg, &fman->qmi_regs->fmqm_gc); + } + + deq_th = ioread32be(&fman->qmi_regs->fmqm_gc) & + QMI_CFG_DEQ_MASK; + /* if deq_th is too small, we enlarge it to the min + * value that is still 0. + * depTh may not be larger than 63 + * (fman->state->qmi_max_num_of_tnums-1). + */ + if ((deq_th <= fman->state->accumulated_num_of_deq_tnums) && + (deq_th < fman->state->qmi_max_num_of_tnums - 1)) { + deq_th = fman->state->accumulated_num_of_deq_tnums + 1; + reg = ioread32be(&fman->qmi_regs->fmqm_gc); + reg &= ~QMI_CFG_DEQ_MASK; + reg |= deq_th; + iowrite32be(reg, &fman->qmi_regs->fmqm_gc); + } + } + + err = set_size_of_fifo(fman, port_params->port_id, + &port_params->size_of_fifo, + &port_params->extra_size_of_fifo); + if (err) + goto return_err; + + err = set_num_of_open_dmas(fman, port_params->port_id, + &port_params->num_of_open_dmas, + &port_params->num_of_extra_open_dmas); + if (err) + goto return_err; + + set_port_liodn(fman, port_id, fman->liodn_base[port_id], + fman->liodn_offset[port_id]); + + if (fman->state->rev_info.major < 6) + set_port_order_restoration(fman->fpm_regs, port_id); + + mac_id = hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id); + + if (port_params->max_frame_length >= fman->state->mac_mfl[mac_id]) { + fman->state->port_mfl[mac_id] = port_params->max_frame_length; + } else { + dev_warn(fman->dev, "%s: Port (%d) max_frame_length is smaller than MAC (%d) current MTU\n", + __func__, port_id, mac_id); + err = -EINVAL; + goto return_err; + } + + spin_unlock_irqrestore(&fman->spinlock, flags); + + return 0; + +return_err: + spin_unlock_irqrestore(&fman->spinlock, flags); + return err; +} +EXPORT_SYMBOL(fman_set_port_params); + +/** + * fman_reset_mac + * @fman: A Pointer to FMan device + * @mac_id: MAC id to be reset + * + * Reset a specific MAC + * + * Return: 0 on success; Error code otherwise. + */ +int fman_reset_mac(struct fman *fman, u8 mac_id) +{ + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + u32 msk, timeout = 100; + + if (fman->state->rev_info.major >= 6) { + dev_err(fman->dev, "%s: FMan MAC reset no available for FMan V3!\n", + __func__); + return -EINVAL; + } + + /* Get the relevant bit mask */ + switch (mac_id) { + case 0: + msk = FPM_RSTC_MAC0_RESET; + break; + case 1: + msk = FPM_RSTC_MAC1_RESET; + break; + case 2: + msk = FPM_RSTC_MAC2_RESET; + break; + case 3: + msk = FPM_RSTC_MAC3_RESET; + break; + case 4: + msk = FPM_RSTC_MAC4_RESET; + break; + case 5: + msk = FPM_RSTC_MAC5_RESET; + break; + case 6: + msk = FPM_RSTC_MAC6_RESET; + break; + case 7: + msk = FPM_RSTC_MAC7_RESET; + break; + case 8: + msk = FPM_RSTC_MAC8_RESET; + break; + case 9: + msk = FPM_RSTC_MAC9_RESET; + break; + default: + dev_warn(fman->dev, "%s: Illegal MAC Id [%d]\n", + __func__, mac_id); + return -EINVAL; + } + + /* reset */ + iowrite32be(msk, &fpm_rg->fm_rstc); + while ((ioread32be(&fpm_rg->fm_rstc) & msk) && --timeout) + udelay(10); + + if (!timeout) + return -EIO; + + return 0; +} +EXPORT_SYMBOL(fman_reset_mac); + +/** + * fman_set_mac_max_frame + * @fman: A Pointer to FMan device + * @mac_id: MAC id + * @mfl: Maximum frame length + * + * Set maximum frame length of specific MAC in FMan driver + * + * Return: 0 on success; Error code otherwise. + */ +int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl) +{ + /* if port is already initialized, check that MaxFrameLength is smaller + * or equal to the port's max + */ + if ((!fman->state->port_mfl[mac_id]) || + (mfl <= fman->state->port_mfl[mac_id])) { + fman->state->mac_mfl[mac_id] = mfl; + } else { + dev_warn(fman->dev, "%s: MAC max_frame_length is larger than Port max_frame_length\n", + __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(fman_set_mac_max_frame); + +/** + * fman_get_clock_freq + * @fman: A Pointer to FMan device + * + * Get FMan clock frequency + * + * Return: FMan clock frequency + */ +u16 fman_get_clock_freq(struct fman *fman) +{ + return fman->state->fm_clk_freq; +} + +/** + * fman_get_bmi_max_fifo_size + * @fman: A Pointer to FMan device + * + * Get FMan maximum FIFO size + * + * Return: FMan Maximum FIFO size + */ +u32 fman_get_bmi_max_fifo_size(struct fman *fman) +{ + return fman->state->bmi_max_fifo_size; +} +EXPORT_SYMBOL(fman_get_bmi_max_fifo_size); + +/** + * fman_get_revision + * @fman: - Pointer to the FMan module + * @rev_info: - A structure of revision information parameters. + * + * Returns the FM revision + * + * Allowed only following fman_init(). + * + * Return: 0 on success; Error code otherwise. + */ +void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info) +{ + u32 tmp; + + tmp = ioread32be(&fman->fpm_regs->fm_ip_rev_1); + rev_info->major = (u8)((tmp & FPM_REV1_MAJOR_MASK) >> + FPM_REV1_MAJOR_SHIFT); + rev_info->minor = tmp & FPM_REV1_MINOR_MASK; +} +EXPORT_SYMBOL(fman_get_revision); + +/** + * fman_get_qman_channel_id + * @fman: A Pointer to FMan device + * @port_id: Port id + * + * Get QMan channel ID associated to the Port id + * + * Return: QMan channel ID + */ +u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id) +{ + int i; + + if (fman->state->rev_info.major >= 6) { + static const u32 port_ids[] = { + 0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7 + }; + + for (i = 0; i < fman->state->num_of_qman_channels; i++) { + if (port_ids[i] == port_id) + break; + } + } else { + static const u32 port_ids[] = { + 0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1, + 0x2, 0x3, 0x4, 0x5, 0x7, 0x7 + }; + + for (i = 0; i < fman->state->num_of_qman_channels; i++) { + if (port_ids[i] == port_id) + break; + } + } + + if (i == fman->state->num_of_qman_channels) + return 0; + + return fman->state->qman_channel_base + i; +} +EXPORT_SYMBOL(fman_get_qman_channel_id); + +/** + * fman_get_mem_region + * @fman: A Pointer to FMan device + * + * Get FMan memory region + * + * Return: A structure with FMan memory region information + */ +struct resource *fman_get_mem_region(struct fman *fman) +{ + return fman->state->res; +} +EXPORT_SYMBOL(fman_get_mem_region); + +/* Bootargs defines */ +/* Extra headroom for RX buffers - Default, min and max */ +#define FSL_FM_RX_EXTRA_HEADROOM 64 +#define FSL_FM_RX_EXTRA_HEADROOM_MIN 16 +#define FSL_FM_RX_EXTRA_HEADROOM_MAX 384 + +/* Maximum frame length */ +#define FSL_FM_MAX_FRAME_SIZE 1522 +#define FSL_FM_MAX_POSSIBLE_FRAME_SIZE 9600 +#define FSL_FM_MIN_POSSIBLE_FRAME_SIZE 64 + +/* Extra headroom for Rx buffers. + * FMan is instructed to allocate, on the Rx path, this amount of + * space at the beginning of a data buffer, beside the DPA private + * data area and the IC fields. + * Does not impact Tx buffer layout. + * Configurable from bootargs. 64 by default, it's needed on + * particular forwarding scenarios that add extra headers to the + * forwarded frame. + */ +static int fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM; +module_param(fsl_fm_rx_extra_headroom, int, 0); +MODULE_PARM_DESC(fsl_fm_rx_extra_headroom, "Extra headroom for Rx buffers"); + +/* Max frame size, across all interfaces. + * Configurable from bootargs, to avoid allocating oversized (socket) + * buffers when not using jumbo frames. + * Must be large enough to accommodate the network MTU, but small enough + * to avoid wasting skb memory. + */ +static int fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE; +module_param(fsl_fm_max_frm, int, 0); +MODULE_PARM_DESC(fsl_fm_max_frm, "Maximum frame size, across all interfaces"); + +/** + * fman_get_max_frm + * + * Return: Max frame length configured in the FM driver + */ +u16 fman_get_max_frm(void) +{ + static bool fm_check_mfl; + + if (!fm_check_mfl) { + if (fsl_fm_max_frm > FSL_FM_MAX_POSSIBLE_FRAME_SIZE || + fsl_fm_max_frm < FSL_FM_MIN_POSSIBLE_FRAME_SIZE) { + pr_warn("Invalid fsl_fm_max_frm value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n", + fsl_fm_max_frm, + FSL_FM_MIN_POSSIBLE_FRAME_SIZE, + FSL_FM_MAX_POSSIBLE_FRAME_SIZE, + FSL_FM_MAX_FRAME_SIZE); + fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE; + } + fm_check_mfl = true; + } + + return fsl_fm_max_frm; +} +EXPORT_SYMBOL(fman_get_max_frm); + +/** + * fman_get_rx_extra_headroom + * + * Return: Extra headroom size configured in the FM driver + */ +int fman_get_rx_extra_headroom(void) +{ + static bool fm_check_rx_extra_headroom; + + if (!fm_check_rx_extra_headroom) { + if (fsl_fm_rx_extra_headroom > FSL_FM_RX_EXTRA_HEADROOM_MAX || + fsl_fm_rx_extra_headroom < FSL_FM_RX_EXTRA_HEADROOM_MIN) { + pr_warn("Invalid fsl_fm_rx_extra_headroom value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n", + fsl_fm_rx_extra_headroom, + FSL_FM_RX_EXTRA_HEADROOM_MIN, + FSL_FM_RX_EXTRA_HEADROOM_MAX, + FSL_FM_RX_EXTRA_HEADROOM); + fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM; + } + + fm_check_rx_extra_headroom = true; + fsl_fm_rx_extra_headroom = ALIGN(fsl_fm_rx_extra_headroom, 16); + } + + return fsl_fm_rx_extra_headroom; +} +EXPORT_SYMBOL(fman_get_rx_extra_headroom); + +/** + * fman_bind + * @fm_dev: FMan OF device pointer + * + * Bind to a specific FMan device. + * + * Allowed only after the port was created. + * + * Return: A pointer to the FMan device + */ +struct fman *fman_bind(struct device *fm_dev) +{ + return (struct fman *)(dev_get_drvdata(get_device(fm_dev))); +} +EXPORT_SYMBOL(fman_bind); + +#ifdef CONFIG_DPAA_ERRATUM_A050385 +bool fman_has_errata_a050385(void) +{ + return fman_has_err_a050385; +} +EXPORT_SYMBOL(fman_has_errata_a050385); +#endif + +static irqreturn_t fman_err_irq(int irq, void *handle) +{ + struct fman *fman = (struct fman *)handle; + u32 pending; + struct fman_fpm_regs __iomem *fpm_rg; + irqreturn_t single_ret, ret = IRQ_NONE; + + if (!is_init_done(fman->cfg)) + return IRQ_NONE; + + fpm_rg = fman->fpm_regs; + + /* error interrupts */ + pending = ioread32be(&fpm_rg->fm_epi); + if (!pending) + return IRQ_NONE; + + if (pending & ERR_INTR_EN_BMI) { + single_ret = bmi_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_QMI) { + single_ret = qmi_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_FPM) { + single_ret = fpm_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_DMA) { + single_ret = dma_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MURAM) { + single_ret = muram_err_intr(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + /* MAC error interrupts */ + if (pending & ERR_INTR_EN_MAC0) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 0); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC1) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 1); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC2) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 2); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC3) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 3); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC4) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 4); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC5) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 5); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC6) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 6); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC7) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 7); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC8) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 8); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC9) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 9); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + return ret; +} + +static irqreturn_t fman_irq(int irq, void *handle) +{ + struct fman *fman = (struct fman *)handle; + u32 pending; + struct fman_fpm_regs __iomem *fpm_rg; + irqreturn_t single_ret, ret = IRQ_NONE; + + if (!is_init_done(fman->cfg)) + return IRQ_NONE; + + fpm_rg = fman->fpm_regs; + + /* normal interrupts */ + pending = ioread32be(&fpm_rg->fm_npi); + if (!pending) + return IRQ_NONE; + + if (pending & INTR_EN_QMI) { + single_ret = qmi_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + /* MAC interrupts */ + if (pending & INTR_EN_MAC0) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 0); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC1) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 1); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC2) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 2); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC3) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 3); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC4) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 4); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC5) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 5); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC6) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 6); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC7) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 7); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC8) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 8); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC9) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 9); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + return ret; +} + +static const struct of_device_id fman_muram_match[] = { + { + .compatible = "fsl,fman-muram"}, + {} +}; +MODULE_DEVICE_TABLE(of, fman_muram_match); + +static struct fman *read_dts_node(struct platform_device *of_dev) +{ + struct fman *fman; + struct device_node *fm_node, *muram_node; + struct resource *res; + u32 val, range[2]; + int err, irq; + struct clk *clk; + u32 clk_rate; + phys_addr_t phys_base_addr; + resource_size_t mem_size; + + fman = kzalloc(sizeof(*fman), GFP_KERNEL); + if (!fman) + return NULL; + + fm_node = of_node_get(of_dev->dev.of_node); + + err = of_property_read_u32(fm_node, "cell-index", &val); + if (err) { + dev_err(&of_dev->dev, "%s: failed to read cell-index for %pOF\n", + __func__, fm_node); + goto fman_node_put; + } + fman->dts_params.id = (u8)val; + + /* Get the FM interrupt */ + res = platform_get_resource(of_dev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&of_dev->dev, "%s: Can't get FMan IRQ resource\n", + __func__); + goto fman_node_put; + } + irq = res->start; + + /* Get the FM error interrupt */ + res = platform_get_resource(of_dev, IORESOURCE_IRQ, 1); + if (!res) { + dev_err(&of_dev->dev, "%s: Can't get FMan Error IRQ resource\n", + __func__); + goto fman_node_put; + } + fman->dts_params.err_irq = res->start; + + /* Get the FM address */ + res = platform_get_resource(of_dev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&of_dev->dev, "%s: Can't get FMan memory resource\n", + __func__); + goto fman_node_put; + } + + phys_base_addr = res->start; + mem_size = resource_size(res); + + clk = of_clk_get(fm_node, 0); + if (IS_ERR(clk)) { + dev_err(&of_dev->dev, "%s: Failed to get FM%d clock structure\n", + __func__, fman->dts_params.id); + goto fman_node_put; + } + + clk_rate = clk_get_rate(clk); + if (!clk_rate) { + dev_err(&of_dev->dev, "%s: Failed to determine FM%d clock rate\n", + __func__, fman->dts_params.id); + goto fman_node_put; + } + /* Rounding to MHz */ + fman->dts_params.clk_freq = DIV_ROUND_UP(clk_rate, 1000000); + + err = of_property_read_u32_array(fm_node, "fsl,qman-channel-range", + &range[0], 2); + if (err) { + dev_err(&of_dev->dev, "%s: failed to read fsl,qman-channel-range for %pOF\n", + __func__, fm_node); + goto fman_node_put; + } + fman->dts_params.qman_channel_base = range[0]; + fman->dts_params.num_of_qman_channels = range[1]; + + /* Get the MURAM base address and size */ + muram_node = of_find_matching_node(fm_node, fman_muram_match); + if (!muram_node) { + dev_err(&of_dev->dev, "%s: could not find MURAM node\n", + __func__); + goto fman_free; + } + + err = of_address_to_resource(muram_node, 0, + &fman->dts_params.muram_res); + if (err) { + of_node_put(muram_node); + dev_err(&of_dev->dev, "%s: of_address_to_resource() = %d\n", + __func__, err); + goto fman_free; + } + + of_node_put(muram_node); + + err = devm_request_irq(&of_dev->dev, irq, fman_irq, IRQF_SHARED, + "fman", fman); + if (err < 0) { + dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n", + __func__, irq, err); + goto fman_free; + } + + if (fman->dts_params.err_irq != 0) { + err = devm_request_irq(&of_dev->dev, fman->dts_params.err_irq, + fman_err_irq, IRQF_SHARED, + "fman-err", fman); + if (err < 0) { + dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n", + __func__, fman->dts_params.err_irq, err); + goto fman_free; + } + } + + fman->dts_params.res = + devm_request_mem_region(&of_dev->dev, phys_base_addr, + mem_size, "fman"); + if (!fman->dts_params.res) { + dev_err(&of_dev->dev, "%s: request_mem_region() failed\n", + __func__); + goto fman_free; + } + + fman->dts_params.base_addr = + devm_ioremap(&of_dev->dev, phys_base_addr, mem_size); + if (!fman->dts_params.base_addr) { + dev_err(&of_dev->dev, "%s: devm_ioremap() failed\n", __func__); + goto fman_free; + } + + fman->dev = &of_dev->dev; + + err = of_platform_populate(fm_node, NULL, NULL, &of_dev->dev); + if (err) { + dev_err(&of_dev->dev, "%s: of_platform_populate() failed\n", + __func__); + goto fman_free; + } + +#ifdef CONFIG_DPAA_ERRATUM_A050385 + fman_has_err_a050385 = + of_property_read_bool(fm_node, "fsl,erratum-a050385"); +#endif + + return fman; + +fman_node_put: + of_node_put(fm_node); +fman_free: + kfree(fman); + return NULL; +} + +static int fman_probe(struct platform_device *of_dev) +{ + struct fman *fman; + struct device *dev; + int err; + + dev = &of_dev->dev; + + fman = read_dts_node(of_dev); + if (!fman) + return -EIO; + + err = fman_config(fman); + if (err) { + dev_err(dev, "%s: FMan config failed\n", __func__); + return -EINVAL; + } + + if (fman_init(fman) != 0) { + dev_err(dev, "%s: FMan init failed\n", __func__); + return -EINVAL; + } + + if (fman->dts_params.err_irq == 0) { + fman_set_exception(fman, FMAN_EX_DMA_BUS_ERROR, false); + fman_set_exception(fman, FMAN_EX_DMA_READ_ECC, false); + fman_set_exception(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC, false); + fman_set_exception(fman, FMAN_EX_DMA_FM_WRITE_ECC, false); + fman_set_exception(fman, FMAN_EX_DMA_SINGLE_PORT_ECC, false); + fman_set_exception(fman, FMAN_EX_FPM_STALL_ON_TASKS, false); + fman_set_exception(fman, FMAN_EX_FPM_SINGLE_ECC, false); + fman_set_exception(fman, FMAN_EX_FPM_DOUBLE_ECC, false); + fman_set_exception(fman, FMAN_EX_QMI_SINGLE_ECC, false); + fman_set_exception(fman, FMAN_EX_QMI_DOUBLE_ECC, false); + fman_set_exception(fman, + FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID, false); + fman_set_exception(fman, FMAN_EX_BMI_LIST_RAM_ECC, false); + fman_set_exception(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC, + false); + fman_set_exception(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC, false); + fman_set_exception(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC, false); + } + + dev_set_drvdata(dev, fman); + + dev_dbg(dev, "FMan%d probed\n", fman->dts_params.id); + + return 0; +} + +static const struct of_device_id fman_match[] = { + { + .compatible = "fsl,fman"}, + {} +}; + +MODULE_DEVICE_TABLE(of, fman_match); + +static struct platform_driver fman_driver = { + .driver = { + .name = "fsl-fman", + .of_match_table = fman_match, + }, + .probe = fman_probe, +}; + +static int __init fman_load(void) +{ + int err; + + pr_debug("FSL DPAA FMan driver\n"); + + err = platform_driver_register(&fman_driver); + if (err < 0) + pr_err("Error, platform_driver_register() = %d\n", err); + + return err; +} +module_init(fman_load); + +static void __exit fman_unload(void) +{ + platform_driver_unregister(&fman_driver); +} +module_exit(fman_unload); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Freescale DPAA Frame Manager driver"); diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h new file mode 100644 index 000000000..f2ede1360 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -0,0 +1,408 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * Copyright 2020 NXP + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FM_H +#define __FM_H + +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> + +/* FM Frame descriptor macros */ +/* Frame queue Context Override */ +#define FM_FD_CMD_FCO 0x80000000 +#define FM_FD_CMD_RPD 0x40000000 /* Read Prepended Data */ +#define FM_FD_CMD_UPD 0x20000000 /* Update Prepended Data */ +#define FM_FD_CMD_DTC 0x10000000 /* Do L4 Checksum */ + +/* TX-Port: Unsupported Format */ +#define FM_FD_ERR_UNSUPPORTED_FORMAT 0x04000000 +/* TX Port: Length Error */ +#define FM_FD_ERR_LENGTH 0x02000000 +#define FM_FD_ERR_DMA 0x01000000 /* DMA Data error */ + +/* IPR frame (not error) */ +#define FM_FD_IPR 0x00000001 +/* IPR non-consistent-sp */ +#define FM_FD_ERR_IPR_NCSP (0x00100000 | FM_FD_IPR) +/* IPR error */ +#define FM_FD_ERR_IPR (0x00200000 | FM_FD_IPR) +/* IPR timeout */ +#define FM_FD_ERR_IPR_TO (0x00300000 | FM_FD_IPR) +/* TX Port: Length Error */ +#define FM_FD_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR) + +/* Rx FIFO overflow, FCS error, code error, running disparity error + * (SGMII and TBI modes), FIFO parity error. PHY Sequence error, + * PHY error control character detected. + */ +#define FM_FD_ERR_PHYSICAL 0x00080000 +/* Frame too long OR Frame size exceeds max_length_frame */ +#define FM_FD_ERR_SIZE 0x00040000 +/* classification discard */ +#define FM_FD_ERR_CLS_DISCARD 0x00020000 +/* Extract Out of Frame */ +#define FM_FD_ERR_EXTRACTION 0x00008000 +/* No Scheme Selected */ +#define FM_FD_ERR_NO_SCHEME 0x00004000 +/* Keysize Overflow */ +#define FM_FD_ERR_KEYSIZE_OVERFLOW 0x00002000 +/* Frame color is red */ +#define FM_FD_ERR_COLOR_RED 0x00000800 +/* Frame color is yellow */ +#define FM_FD_ERR_COLOR_YELLOW 0x00000400 +/* Parser Time out Exceed */ +#define FM_FD_ERR_PRS_TIMEOUT 0x00000080 +/* Invalid Soft Parser instruction */ +#define FM_FD_ERR_PRS_ILL_INSTRUCT 0x00000040 +/* Header error was identified during parsing */ +#define FM_FD_ERR_PRS_HDR_ERR 0x00000020 +/* Frame parsed beyind 256 first bytes */ +#define FM_FD_ERR_BLOCK_LIMIT_EXCEEDED 0x00000008 + +/* non Frame-Manager error */ +#define FM_FD_RX_STATUS_ERR_NON_FM 0x00400000 + +/* FMan driver defines */ +#define FMAN_BMI_FIFO_UNITS 0x100 +#define OFFSET_UNITS 16 + +/* BMan defines */ +#define BM_MAX_NUM_OF_POOLS 64 /* Buffers pools */ +#define FMAN_PORT_MAX_EXT_POOLS_NUM 8 /* External BM pools per Rx port */ + +struct fman; /* FMan data */ + +/* Enum for defining port types */ +enum fman_port_type { + FMAN_PORT_TYPE_TX = 0, /* TX Port */ + FMAN_PORT_TYPE_RX, /* RX Port */ +}; + +struct fman_rev_info { + u8 major; /* Major revision */ + u8 minor; /* Minor revision */ +}; + +enum fman_exceptions { + FMAN_EX_DMA_BUS_ERROR = 0, /* DMA bus error. */ + FMAN_EX_DMA_READ_ECC, /* Read Buffer ECC error */ + FMAN_EX_DMA_SYSTEM_WRITE_ECC, /* Write Buffer ECC err on sys side */ + FMAN_EX_DMA_FM_WRITE_ECC, /* Write Buffer ECC error on FM side */ + FMAN_EX_DMA_SINGLE_PORT_ECC, /* Single Port ECC error on FM side */ + FMAN_EX_FPM_STALL_ON_TASKS, /* Stall of tasks on FPM */ + FMAN_EX_FPM_SINGLE_ECC, /* Single ECC on FPM. */ + FMAN_EX_FPM_DOUBLE_ECC, /* Double ECC error on FPM ram access */ + FMAN_EX_QMI_SINGLE_ECC, /* Single ECC on QMI. */ + FMAN_EX_QMI_DOUBLE_ECC, /* Double bit ECC occurred on QMI */ + FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID,/* DeQ from unknown port id */ + FMAN_EX_BMI_LIST_RAM_ECC, /* Linked List RAM ECC error */ + FMAN_EX_BMI_STORAGE_PROFILE_ECC,/* storage profile */ + FMAN_EX_BMI_STATISTICS_RAM_ECC,/* Statistics RAM ECC Err Enable */ + FMAN_EX_BMI_DISPATCH_RAM_ECC, /* Dispatch RAM ECC Error Enable */ + FMAN_EX_IRAM_ECC, /* Double bit ECC occurred on IRAM */ + FMAN_EX_MURAM_ECC /* Double bit ECC occurred on MURAM */ +}; + +/* Parse results memory layout */ +struct fman_prs_result { + u8 lpid; /* Logical port id */ + u8 shimr; /* Shim header result */ + __be16 l2r; /* Layer 2 result */ + __be16 l3r; /* Layer 3 result */ + u8 l4r; /* Layer 4 result */ + u8 cplan; /* Classification plan id */ + __be16 nxthdr; /* Next Header */ + __be16 cksum; /* Running-sum */ + /* Flags&fragment-offset field of the last IP-header */ + __be16 flags_frag_off; + /* Routing type field of a IPV6 routing extension header */ + u8 route_type; + /* Routing Extension Header Present; last bit is IP valid */ + u8 rhp_ip_valid; + u8 shim_off[2]; /* Shim offset */ + u8 ip_pid_off; /* IP PID (last IP-proto) offset */ + u8 eth_off; /* ETH offset */ + u8 llc_snap_off; /* LLC_SNAP offset */ + u8 vlan_off[2]; /* VLAN offset */ + u8 etype_off; /* ETYPE offset */ + u8 pppoe_off; /* PPP offset */ + u8 mpls_off[2]; /* MPLS offset */ + u8 ip_off[2]; /* IP offset */ + u8 gre_off; /* GRE offset */ + u8 l4_off; /* Layer 4 offset */ + u8 nxthdr_off; /* Parser end point */ +}; + +/* A structure for defining buffer prefix area content. */ +struct fman_buffer_prefix_content { + /* Number of bytes to be left at the beginning of the external + * buffer; Note that the private-area will start from the base + * of the buffer address. + */ + u16 priv_data_size; + /* true to pass the parse result to/from the FM; + * User may use FM_PORT_GetBufferPrsResult() in + * order to get the parser-result from a buffer. + */ + bool pass_prs_result; + /* true to pass the timeStamp to/from the FM User */ + bool pass_time_stamp; + /* true to pass the KG hash result to/from the FM User may + * use FM_PORT_GetBufferHashResult() in order to get the + * parser-result from a buffer. + */ + bool pass_hash_result; + /* Add all other Internal-Context information: AD, + * hash-result, key, etc. + */ + u16 data_align; +}; + +/* A structure of information about each of the external + * buffer pools used by a port or storage-profile. + */ +struct fman_ext_pool_params { + u8 id; /* External buffer pool id */ + u16 size; /* External buffer pool buffer size */ +}; + +/* A structure for informing the driver about the external + * buffer pools allocated in the BM and used by a port or a + * storage-profile. + */ +struct fman_ext_pools { + u8 num_of_pools_used; /* Number of pools use by this port */ + struct fman_ext_pool_params ext_buf_pool[FMAN_PORT_MAX_EXT_POOLS_NUM]; + /* Parameters for each port */ +}; + +/* A structure for defining BM pool depletion criteria */ +struct fman_buf_pool_depletion { + /* select mode in which pause frames will be sent after a + * number of pools (all together!) are depleted + */ + bool pools_grp_mode_enable; + /* the number of depleted pools that will invoke pause + * frames transmission. + */ + u8 num_of_pools; + /* For each pool, true if it should be considered for + * depletion (Note - this pool must be used by this port!). + */ + bool pools_to_consider[BM_MAX_NUM_OF_POOLS]; + /* select mode in which pause frames will be sent + * after a single-pool is depleted; + */ + bool single_pool_mode_enable; + /* For each pool, true if it should be considered + * for depletion (Note - this pool must be used by this port!) + */ + bool pools_to_consider_for_single_mode[BM_MAX_NUM_OF_POOLS]; +}; + +/* Enum for inter-module interrupts registration */ +enum fman_event_modules { + FMAN_MOD_MAC = 0, /* MAC event */ + FMAN_MOD_FMAN_CTRL, /* FMAN Controller */ + FMAN_MOD_DUMMY_LAST +}; + +/* Enum for interrupts types */ +enum fman_intr_type { + FMAN_INTR_TYPE_ERR, + FMAN_INTR_TYPE_NORMAL +}; + +/* Enum for inter-module interrupts registration */ +enum fman_inter_module_event { + FMAN_EV_ERR_MAC0 = 0, /* MAC 0 error event */ + FMAN_EV_ERR_MAC1, /* MAC 1 error event */ + FMAN_EV_ERR_MAC2, /* MAC 2 error event */ + FMAN_EV_ERR_MAC3, /* MAC 3 error event */ + FMAN_EV_ERR_MAC4, /* MAC 4 error event */ + FMAN_EV_ERR_MAC5, /* MAC 5 error event */ + FMAN_EV_ERR_MAC6, /* MAC 6 error event */ + FMAN_EV_ERR_MAC7, /* MAC 7 error event */ + FMAN_EV_ERR_MAC8, /* MAC 8 error event */ + FMAN_EV_ERR_MAC9, /* MAC 9 error event */ + FMAN_EV_MAC0, /* MAC 0 event (Magic packet detection) */ + FMAN_EV_MAC1, /* MAC 1 event (Magic packet detection) */ + FMAN_EV_MAC2, /* MAC 2 (Magic packet detection) */ + FMAN_EV_MAC3, /* MAC 3 (Magic packet detection) */ + FMAN_EV_MAC4, /* MAC 4 (Magic packet detection) */ + FMAN_EV_MAC5, /* MAC 5 (Magic packet detection) */ + FMAN_EV_MAC6, /* MAC 6 (Magic packet detection) */ + FMAN_EV_MAC7, /* MAC 7 (Magic packet detection) */ + FMAN_EV_MAC8, /* MAC 8 event (Magic packet detection) */ + FMAN_EV_MAC9, /* MAC 9 event (Magic packet detection) */ + FMAN_EV_FMAN_CTRL_0, /* Fman controller event 0 */ + FMAN_EV_FMAN_CTRL_1, /* Fman controller event 1 */ + FMAN_EV_FMAN_CTRL_2, /* Fman controller event 2 */ + FMAN_EV_FMAN_CTRL_3, /* Fman controller event 3 */ + FMAN_EV_CNT +}; + +struct fman_intr_src { + void (*isr_cb)(void *src_arg); + void *src_handle; +}; + +/** fman_exceptions_cb + * fman - Pointer to FMan + * exception - The exception. + * + * Exceptions user callback routine, will be called upon an exception + * passing the exception identification. + * + * Return: irq status + */ +typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman, + enum fman_exceptions exception); +/** fman_bus_error_cb + * fman - Pointer to FMan + * port_id - Port id + * addr - Address that caused the error + * tnum - Owner of error + * liodn - Logical IO device number + * + * Bus error user callback routine, will be called upon bus error, + * passing parameters describing the errors and the owner. + * + * Return: IRQ status + */ +typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id, + u64 addr, u8 tnum, u16 liodn); + +/* Structure that holds information received from device tree */ +struct fman_dts_params { + void __iomem *base_addr; /* FMan virtual address */ + struct resource *res; /* FMan memory resource */ + u8 id; /* FMan ID */ + + int err_irq; /* FMan Error IRQ */ + + u16 clk_freq; /* FMan clock freq (In Mhz) */ + + u32 qman_channel_base; /* QMan channels base */ + u32 num_of_qman_channels; /* Number of QMan channels */ + + struct resource muram_res; /* MURAM resource */ +}; + +struct fman { + struct device *dev; + void __iomem *base_addr; + struct fman_intr_src intr_mng[FMAN_EV_CNT]; + + struct fman_fpm_regs __iomem *fpm_regs; + struct fman_bmi_regs __iomem *bmi_regs; + struct fman_qmi_regs __iomem *qmi_regs; + struct fman_dma_regs __iomem *dma_regs; + struct fman_hwp_regs __iomem *hwp_regs; + struct fman_kg_regs __iomem *kg_regs; + fman_exceptions_cb *exception_cb; + fman_bus_error_cb *bus_error_cb; + /* Spinlock for FMan use */ + spinlock_t spinlock; + struct fman_state_struct *state; + + struct fman_cfg *cfg; + struct muram_info *muram; + struct fman_keygen *keygen; + /* cam section in muram */ + unsigned long cam_offset; + size_t cam_size; + /* Fifo in MURAM */ + unsigned long fifo_offset; + size_t fifo_size; + + u32 liodn_base[64]; + u32 liodn_offset[64]; + + struct fman_dts_params dts_params; +}; + +/* Structure for port-FM communication during fman_port_init. */ +struct fman_port_init_params { + u8 port_id; /* port Id */ + enum fman_port_type port_type; /* Port type */ + u16 port_speed; /* Port speed */ + u16 liodn_offset; /* Port's requested resource */ + u8 num_of_tasks; /* Port's requested resource */ + u8 num_of_extra_tasks; /* Port's requested resource */ + u8 num_of_open_dmas; /* Port's requested resource */ + u8 num_of_extra_open_dmas; /* Port's requested resource */ + u32 size_of_fifo; /* Port's requested resource */ + u32 extra_size_of_fifo; /* Port's requested resource */ + u8 deq_pipeline_depth; /* Port's requested resource */ + u16 max_frame_length; /* Port's max frame length. */ + u16 liodn_base; + /* LIODN base for this port, to be used together with LIODN offset. */ +}; + +void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info); + +void fman_register_intr(struct fman *fman, enum fman_event_modules mod, + u8 mod_id, enum fman_intr_type intr_type, + void (*f_isr)(void *h_src_arg), void *h_src_arg); + +void fman_unregister_intr(struct fman *fman, enum fman_event_modules mod, + u8 mod_id, enum fman_intr_type intr_type); + +int fman_set_port_params(struct fman *fman, + struct fman_port_init_params *port_params); + +int fman_reset_mac(struct fman *fman, u8 mac_id); + +u16 fman_get_clock_freq(struct fman *fman); + +u32 fman_get_bmi_max_fifo_size(struct fman *fman); + +int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl); + +u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id); + +struct resource *fman_get_mem_region(struct fman *fman); + +u16 fman_get_max_frm(void); + +int fman_get_rx_extra_headroom(void); + +#ifdef CONFIG_DPAA_ERRATUM_A050385 +bool fman_has_errata_a050385(void); +#endif + +struct fman *fman_bind(struct device *dev); + +#endif /* __FM_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c new file mode 100644 index 000000000..bce3c9398 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -0,0 +1,1563 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_dtsec.h" +#include "fman.h" + +#include <linux/slab.h> +#include <linux/bitrev.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/phy.h> +#include <linux/crc32.h> +#include <linux/of_mdio.h> +#include <linux/mii.h> + +/* TBI register addresses */ +#define MII_TBICON 0x11 + +/* TBICON register bit fields */ +#define TBICON_SOFT_RESET 0x8000 /* Soft reset */ +#define TBICON_DISABLE_RX_DIS 0x2000 /* Disable receive disparity */ +#define TBICON_DISABLE_TX_DIS 0x1000 /* Disable transmit disparity */ +#define TBICON_AN_SENSE 0x0100 /* Auto-negotiation sense enable */ +#define TBICON_CLK_SELECT 0x0020 /* Clock select */ +#define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */ + +#define TBIANA_SGMII 0x4001 +#define TBIANA_1000X 0x01a0 + +/* Interrupt Mask Register (IMASK) */ +#define DTSEC_IMASK_BREN 0x80000000 +#define DTSEC_IMASK_RXCEN 0x40000000 +#define DTSEC_IMASK_MSROEN 0x04000000 +#define DTSEC_IMASK_GTSCEN 0x02000000 +#define DTSEC_IMASK_BTEN 0x01000000 +#define DTSEC_IMASK_TXCEN 0x00800000 +#define DTSEC_IMASK_TXEEN 0x00400000 +#define DTSEC_IMASK_LCEN 0x00040000 +#define DTSEC_IMASK_CRLEN 0x00020000 +#define DTSEC_IMASK_XFUNEN 0x00010000 +#define DTSEC_IMASK_ABRTEN 0x00008000 +#define DTSEC_IMASK_IFERREN 0x00004000 +#define DTSEC_IMASK_MAGEN 0x00000800 +#define DTSEC_IMASK_MMRDEN 0x00000400 +#define DTSEC_IMASK_MMWREN 0x00000200 +#define DTSEC_IMASK_GRSCEN 0x00000100 +#define DTSEC_IMASK_TDPEEN 0x00000002 +#define DTSEC_IMASK_RDPEEN 0x00000001 + +#define DTSEC_EVENTS_MASK \ + ((u32)(DTSEC_IMASK_BREN | \ + DTSEC_IMASK_RXCEN | \ + DTSEC_IMASK_BTEN | \ + DTSEC_IMASK_TXCEN | \ + DTSEC_IMASK_TXEEN | \ + DTSEC_IMASK_ABRTEN | \ + DTSEC_IMASK_LCEN | \ + DTSEC_IMASK_CRLEN | \ + DTSEC_IMASK_XFUNEN | \ + DTSEC_IMASK_IFERREN | \ + DTSEC_IMASK_MAGEN | \ + DTSEC_IMASK_TDPEEN | \ + DTSEC_IMASK_RDPEEN)) + +/* dtsec timestamp event bits */ +#define TMR_PEMASK_TSREEN 0x00010000 +#define TMR_PEVENT_TSRE 0x00010000 + +/* Group address bit indication */ +#define MAC_GROUP_ADDRESS 0x0000010000000000ULL + +/* Defaults */ +#define DEFAULT_HALFDUP_RETRANSMIT 0xf +#define DEFAULT_HALFDUP_COLL_WINDOW 0x37 +#define DEFAULT_TX_PAUSE_TIME 0xf000 +#define DEFAULT_RX_PREPEND 0 +#define DEFAULT_PREAMBLE_LEN 7 +#define DEFAULT_TX_PAUSE_TIME_EXTD 0 +#define DEFAULT_NON_BACK_TO_BACK_IPG1 0x40 +#define DEFAULT_NON_BACK_TO_BACK_IPG2 0x60 +#define DEFAULT_MIN_IFG_ENFORCEMENT 0x50 +#define DEFAULT_BACK_TO_BACK_IPG 0x60 +#define DEFAULT_MAXIMUM_FRAME 0x600 + +/* register related defines (bits, field offsets..) */ +#define DTSEC_ID2_INT_REDUCED_OFF 0x00010000 + +#define DTSEC_ECNTRL_GMIIM 0x00000040 +#define DTSEC_ECNTRL_TBIM 0x00000020 +#define DTSEC_ECNTRL_SGMIIM 0x00000002 +#define DTSEC_ECNTRL_RPM 0x00000010 +#define DTSEC_ECNTRL_R100M 0x00000008 +#define DTSEC_ECNTRL_QSGMIIM 0x00000001 + +#define TCTRL_TTSE 0x00000040 +#define TCTRL_GTS 0x00000020 + +#define RCTRL_PAL_MASK 0x001f0000 +#define RCTRL_PAL_SHIFT 16 +#define RCTRL_GHTX 0x00000400 +#define RCTRL_RTSE 0x00000040 +#define RCTRL_GRS 0x00000020 +#define RCTRL_MPROM 0x00000008 +#define RCTRL_RSF 0x00000004 +#define RCTRL_UPROM 0x00000001 + +#define MACCFG1_SOFT_RESET 0x80000000 +#define MACCFG1_RX_FLOW 0x00000020 +#define MACCFG1_TX_FLOW 0x00000010 +#define MACCFG1_TX_EN 0x00000001 +#define MACCFG1_RX_EN 0x00000004 + +#define MACCFG2_NIBBLE_MODE 0x00000100 +#define MACCFG2_BYTE_MODE 0x00000200 +#define MACCFG2_PAD_CRC_EN 0x00000004 +#define MACCFG2_FULL_DUPLEX 0x00000001 +#define MACCFG2_PREAMBLE_LENGTH_MASK 0x0000f000 +#define MACCFG2_PREAMBLE_LENGTH_SHIFT 12 + +#define IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT 24 +#define IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT 16 +#define IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT 8 + +#define IPGIFG_NON_BACK_TO_BACK_IPG_1 0x7F000000 +#define IPGIFG_NON_BACK_TO_BACK_IPG_2 0x007F0000 +#define IPGIFG_MIN_IFG_ENFORCEMENT 0x0000FF00 +#define IPGIFG_BACK_TO_BACK_IPG 0x0000007F + +#define HAFDUP_EXCESS_DEFER 0x00010000 +#define HAFDUP_COLLISION_WINDOW 0x000003ff +#define HAFDUP_RETRANSMISSION_MAX_SHIFT 12 +#define HAFDUP_RETRANSMISSION_MAX 0x0000f000 + +#define NUM_OF_HASH_REGS 8 /* Number of hash table registers */ + +#define PTV_PTE_MASK 0xffff0000 +#define PTV_PT_MASK 0x0000ffff +#define PTV_PTE_SHIFT 16 + +#define MAX_PACKET_ALIGNMENT 31 +#define MAX_INTER_PACKET_GAP 0x7f +#define MAX_RETRANSMISSION 0x0f +#define MAX_COLLISION_WINDOW 0x03ff + +/* Hash table size (32 bits*8 regs) */ +#define DTSEC_HASH_TABLE_SIZE 256 +/* Extended Hash table size (32 bits*16 regs) */ +#define EXTENDED_HASH_TABLE_SIZE 512 + +/* dTSEC Memory Map registers */ +struct dtsec_regs { + /* dTSEC General Control and Status Registers */ + u32 tsec_id; /* 0x000 ETSEC_ID register */ + u32 tsec_id2; /* 0x004 ETSEC_ID2 register */ + u32 ievent; /* 0x008 Interrupt event register */ + u32 imask; /* 0x00C Interrupt mask register */ + u32 reserved0010[1]; + u32 ecntrl; /* 0x014 E control register */ + u32 ptv; /* 0x018 Pause time value register */ + u32 tbipa; /* 0x01C TBI PHY address register */ + u32 tmr_ctrl; /* 0x020 Time-stamp Control register */ + u32 tmr_pevent; /* 0x024 Time-stamp event register */ + u32 tmr_pemask; /* 0x028 Timer event mask register */ + u32 reserved002c[5]; + u32 tctrl; /* 0x040 Transmit control register */ + u32 reserved0044[3]; + u32 rctrl; /* 0x050 Receive control register */ + u32 reserved0054[11]; + u32 igaddr[8]; /* 0x080-0x09C Individual/group address */ + u32 gaddr[8]; /* 0x0A0-0x0BC Group address registers 0-7 */ + u32 reserved00c0[16]; + u32 maccfg1; /* 0x100 MAC configuration #1 */ + u32 maccfg2; /* 0x104 MAC configuration #2 */ + u32 ipgifg; /* 0x108 IPG/IFG */ + u32 hafdup; /* 0x10C Half-duplex */ + u32 maxfrm; /* 0x110 Maximum frame */ + u32 reserved0114[10]; + u32 ifstat; /* 0x13C Interface status */ + u32 macstnaddr1; /* 0x140 Station Address,part 1 */ + u32 macstnaddr2; /* 0x144 Station Address,part 2 */ + struct { + u32 exact_match1; /* octets 1-4 */ + u32 exact_match2; /* octets 5-6 */ + } macaddr[15]; /* 0x148-0x1BC mac exact match addresses 1-15 */ + u32 reserved01c0[16]; + u32 tr64; /* 0x200 Tx and Rx 64 byte frame counter */ + u32 tr127; /* 0x204 Tx and Rx 65 to 127 byte frame counter */ + u32 tr255; /* 0x208 Tx and Rx 128 to 255 byte frame counter */ + u32 tr511; /* 0x20C Tx and Rx 256 to 511 byte frame counter */ + u32 tr1k; /* 0x210 Tx and Rx 512 to 1023 byte frame counter */ + u32 trmax; /* 0x214 Tx and Rx 1024 to 1518 byte frame counter */ + u32 trmgv; + /* 0x218 Tx and Rx 1519 to 1522 byte good VLAN frame count */ + u32 rbyt; /* 0x21C receive byte counter */ + u32 rpkt; /* 0x220 receive packet counter */ + u32 rfcs; /* 0x224 receive FCS error counter */ + u32 rmca; /* 0x228 RMCA Rx multicast packet counter */ + u32 rbca; /* 0x22C Rx broadcast packet counter */ + u32 rxcf; /* 0x230 Rx control frame packet counter */ + u32 rxpf; /* 0x234 Rx pause frame packet counter */ + u32 rxuo; /* 0x238 Rx unknown OP code counter */ + u32 raln; /* 0x23C Rx alignment error counter */ + u32 rflr; /* 0x240 Rx frame length error counter */ + u32 rcde; /* 0x244 Rx code error counter */ + u32 rcse; /* 0x248 Rx carrier sense error counter */ + u32 rund; /* 0x24C Rx undersize packet counter */ + u32 rovr; /* 0x250 Rx oversize packet counter */ + u32 rfrg; /* 0x254 Rx fragments counter */ + u32 rjbr; /* 0x258 Rx jabber counter */ + u32 rdrp; /* 0x25C Rx drop */ + u32 tbyt; /* 0x260 Tx byte counter */ + u32 tpkt; /* 0x264 Tx packet counter */ + u32 tmca; /* 0x268 Tx multicast packet counter */ + u32 tbca; /* 0x26C Tx broadcast packet counter */ + u32 txpf; /* 0x270 Tx pause control frame counter */ + u32 tdfr; /* 0x274 Tx deferral packet counter */ + u32 tedf; /* 0x278 Tx excessive deferral packet counter */ + u32 tscl; /* 0x27C Tx single collision packet counter */ + u32 tmcl; /* 0x280 Tx multiple collision packet counter */ + u32 tlcl; /* 0x284 Tx late collision packet counter */ + u32 txcl; /* 0x288 Tx excessive collision packet counter */ + u32 tncl; /* 0x28C Tx total collision counter */ + u32 reserved0290[1]; + u32 tdrp; /* 0x294 Tx drop frame counter */ + u32 tjbr; /* 0x298 Tx jabber frame counter */ + u32 tfcs; /* 0x29C Tx FCS error counter */ + u32 txcf; /* 0x2A0 Tx control frame counter */ + u32 tovr; /* 0x2A4 Tx oversize frame counter */ + u32 tund; /* 0x2A8 Tx undersize frame counter */ + u32 tfrg; /* 0x2AC Tx fragments frame counter */ + u32 car1; /* 0x2B0 carry register one register* */ + u32 car2; /* 0x2B4 carry register two register* */ + u32 cam1; /* 0x2B8 carry register one mask register */ + u32 cam2; /* 0x2BC carry register two mask register */ + u32 reserved02c0[848]; +}; + +/* struct dtsec_cfg - dTSEC configuration + * Transmit half-duplex flow control, under software control for 10/100-Mbps + * half-duplex media. If set, back pressure is applied to media by raising + * carrier. + * halfdup_retransmit: + * Number of retransmission attempts following a collision. + * If this is exceeded dTSEC aborts transmission due to excessive collisions. + * The standard specifies the attempt limit to be 15. + * halfdup_coll_window: + * The number of bytes of the frame during which collisions may occur. + * The default value of 55 corresponds to the frame byte at the end of the + * standard 512-bit slot time window. If collisions are detected after this + * byte, the late collision event is asserted and transmission of current + * frame is aborted. + * tx_pad_crc: + * Pad and append CRC. If set, the MAC pads all ransmitted short frames and + * appends a CRC to every frame regardless of padding requirement. + * tx_pause_time: + * Transmit pause time value. This pause value is used as part of the pause + * frame to be sent when a transmit pause frame is initiated. + * If set to 0 this disables transmission of pause frames. + * preamble_len: + * Length, in bytes, of the preamble field preceding each Ethernet + * start-of-frame delimiter byte. The default value of 0x7 should be used in + * order to guarantee reliable operation with IEEE 802.3 compliant hardware. + * rx_prepend: + * Packet alignment padding length. The specified number of bytes (1-31) + * of zero padding are inserted before the start of each received frame. + * For Ethernet, where optional preamble extraction is enabled, the padding + * appears before the preamble, otherwise the padding precedes the + * layer 2 header. + * + * This structure contains basic dTSEC configuration and must be passed to + * init() function. A default set of configuration values can be + * obtained by calling set_dflts(). + */ +struct dtsec_cfg { + u16 halfdup_retransmit; + u16 halfdup_coll_window; + bool tx_pad_crc; + u16 tx_pause_time; + bool ptp_tsu_en; + bool ptp_exception_en; + u32 preamble_len; + u32 rx_prepend; + u16 tx_pause_time_extd; + u16 maximum_frame; + u32 non_back_to_back_ipg1; + u32 non_back_to_back_ipg2; + u32 min_ifg_enforcement; + u32 back_to_back_ipg; +}; + +struct fman_mac { + /* pointer to dTSEC memory mapped registers */ + struct dtsec_regs __iomem *regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + phy_interface_t phy_if; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* Number of individual addresses in registers for this station */ + u8 num_of_ind_addr_in_regs; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + bool ptp_tsu_enabled; + bool en_tsu_err_exception; + struct dtsec_cfg *dtsec_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy_device *tbiphy; +}; + +static void set_dflts(struct dtsec_cfg *cfg) +{ + cfg->halfdup_retransmit = DEFAULT_HALFDUP_RETRANSMIT; + cfg->halfdup_coll_window = DEFAULT_HALFDUP_COLL_WINDOW; + cfg->tx_pad_crc = true; + cfg->tx_pause_time = DEFAULT_TX_PAUSE_TIME; + /* PHY address 0 is reserved (DPAA RM) */ + cfg->rx_prepend = DEFAULT_RX_PREPEND; + cfg->ptp_tsu_en = true; + cfg->ptp_exception_en = true; + cfg->preamble_len = DEFAULT_PREAMBLE_LEN; + cfg->tx_pause_time_extd = DEFAULT_TX_PAUSE_TIME_EXTD; + cfg->non_back_to_back_ipg1 = DEFAULT_NON_BACK_TO_BACK_IPG1; + cfg->non_back_to_back_ipg2 = DEFAULT_NON_BACK_TO_BACK_IPG2; + cfg->min_ifg_enforcement = DEFAULT_MIN_IFG_ENFORCEMENT; + cfg->back_to_back_ipg = DEFAULT_BACK_TO_BACK_IPG; + cfg->maximum_frame = DEFAULT_MAXIMUM_FRAME; +} + +static void set_mac_address(struct dtsec_regs __iomem *regs, u8 *adr) +{ + u32 tmp; + + tmp = (u32)((adr[5] << 24) | + (adr[4] << 16) | (adr[3] << 8) | adr[2]); + iowrite32be(tmp, ®s->macstnaddr1); + + tmp = (u32)((adr[1] << 24) | (adr[0] << 16)); + iowrite32be(tmp, ®s->macstnaddr2); +} + +static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, + phy_interface_t iface, u16 iface_speed, u64 addr, + u32 exception_mask, u8 tbi_addr) +{ + bool is_rgmii, is_sgmii, is_qsgmii; + enet_addr_t eth_addr; + u32 tmp; + int i; + + /* Soft reset */ + iowrite32be(MACCFG1_SOFT_RESET, ®s->maccfg1); + iowrite32be(0, ®s->maccfg1); + + /* dtsec_id2 */ + tmp = ioread32be(®s->tsec_id2); + + /* check RGMII support */ + if (iface == PHY_INTERFACE_MODE_RGMII || + iface == PHY_INTERFACE_MODE_RGMII_ID || + iface == PHY_INTERFACE_MODE_RGMII_RXID || + iface == PHY_INTERFACE_MODE_RGMII_TXID || + iface == PHY_INTERFACE_MODE_RMII) + if (tmp & DTSEC_ID2_INT_REDUCED_OFF) + return -EINVAL; + + if (iface == PHY_INTERFACE_MODE_SGMII || + iface == PHY_INTERFACE_MODE_MII) + if (tmp & DTSEC_ID2_INT_REDUCED_OFF) + return -EINVAL; + + is_rgmii = iface == PHY_INTERFACE_MODE_RGMII || + iface == PHY_INTERFACE_MODE_RGMII_ID || + iface == PHY_INTERFACE_MODE_RGMII_RXID || + iface == PHY_INTERFACE_MODE_RGMII_TXID; + is_sgmii = iface == PHY_INTERFACE_MODE_SGMII; + is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII; + + tmp = 0; + if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII) + tmp |= DTSEC_ECNTRL_GMIIM; + if (is_sgmii) + tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM); + if (is_qsgmii) + tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM | + DTSEC_ECNTRL_QSGMIIM); + if (is_rgmii) + tmp |= DTSEC_ECNTRL_RPM; + if (iface_speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + + iowrite32be(tmp, ®s->ecntrl); + + tmp = 0; + + if (cfg->tx_pause_time) + tmp |= cfg->tx_pause_time; + if (cfg->tx_pause_time_extd) + tmp |= cfg->tx_pause_time_extd << PTV_PTE_SHIFT; + iowrite32be(tmp, ®s->ptv); + + tmp = 0; + tmp |= (cfg->rx_prepend << RCTRL_PAL_SHIFT) & RCTRL_PAL_MASK; + /* Accept short frames */ + tmp |= RCTRL_RSF; + + iowrite32be(tmp, ®s->rctrl); + + /* Assign a Phy Address to the TBI (TBIPA). + * Done also in cases where TBI is not selected to avoid conflict with + * the external PHY's Physical address + */ + iowrite32be(tbi_addr, ®s->tbipa); + + iowrite32be(0, ®s->tmr_ctrl); + + if (cfg->ptp_tsu_en) { + tmp = 0; + tmp |= TMR_PEVENT_TSRE; + iowrite32be(tmp, ®s->tmr_pevent); + + if (cfg->ptp_exception_en) { + tmp = 0; + tmp |= TMR_PEMASK_TSREEN; + iowrite32be(tmp, ®s->tmr_pemask); + } + } + + tmp = 0; + tmp |= MACCFG1_RX_FLOW; + tmp |= MACCFG1_TX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + + tmp = 0; + + if (iface_speed < SPEED_1000) + tmp |= MACCFG2_NIBBLE_MODE; + else if (iface_speed == SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + + tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) & + MACCFG2_PREAMBLE_LENGTH_MASK; + if (cfg->tx_pad_crc) + tmp |= MACCFG2_PAD_CRC_EN; + /* Full Duplex */ + tmp |= MACCFG2_FULL_DUPLEX; + iowrite32be(tmp, ®s->maccfg2); + + tmp = (((cfg->non_back_to_back_ipg1 << + IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT) + & IPGIFG_NON_BACK_TO_BACK_IPG_1) + | ((cfg->non_back_to_back_ipg2 << + IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT) + & IPGIFG_NON_BACK_TO_BACK_IPG_2) + | ((cfg->min_ifg_enforcement << IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT) + & IPGIFG_MIN_IFG_ENFORCEMENT) + | (cfg->back_to_back_ipg & IPGIFG_BACK_TO_BACK_IPG)); + iowrite32be(tmp, ®s->ipgifg); + + tmp = 0; + tmp |= HAFDUP_EXCESS_DEFER; + tmp |= ((cfg->halfdup_retransmit << HAFDUP_RETRANSMISSION_MAX_SHIFT) + & HAFDUP_RETRANSMISSION_MAX); + tmp |= (cfg->halfdup_coll_window & HAFDUP_COLLISION_WINDOW); + + iowrite32be(tmp, ®s->hafdup); + + /* Initialize Maximum frame length */ + iowrite32be(cfg->maximum_frame, ®s->maxfrm); + + iowrite32be(0xffffffff, ®s->cam1); + iowrite32be(0xffffffff, ®s->cam2); + + iowrite32be(exception_mask, ®s->imask); + + iowrite32be(0xffffffff, ®s->ievent); + + if (addr) { + MAKE_ENET_ADDR_FROM_UINT64(addr, eth_addr); + set_mac_address(regs, (u8 *)eth_addr); + } + + /* HASH */ + for (i = 0; i < NUM_OF_HASH_REGS; i++) { + /* Initialize IADDRx */ + iowrite32be(0, ®s->igaddr[i]); + /* Initialize GADDRx */ + iowrite32be(0, ®s->gaddr[i]); + } + + return 0; +} + +static void set_bucket(struct dtsec_regs __iomem *regs, int bucket, + bool enable) +{ + int reg_idx = (bucket >> 5) & 0xf; + int bit_idx = bucket & 0x1f; + u32 bit_mask = 0x80000000 >> bit_idx; + u32 __iomem *reg; + + if (reg_idx > 7) + reg = ®s->gaddr[reg_idx - 8]; + else + reg = ®s->igaddr[reg_idx]; + + if (enable) + iowrite32be(ioread32be(reg) | bit_mask, reg); + else + iowrite32be(ioread32be(reg) & (~bit_mask), reg); +} + +static int check_init_parameters(struct fman_mac *dtsec) +{ + if (dtsec->max_speed >= SPEED_10000) { + pr_err("1G MAC driver supports 1G or lower speeds\n"); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->rx_prepend > + MAX_PACKET_ALIGNMENT) { + pr_err("packetAlignmentPadding can't be > than %d\n", + MAX_PACKET_ALIGNMENT); + return -EINVAL; + } + if (((dtsec->dtsec_drv_param)->non_back_to_back_ipg1 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->non_back_to_back_ipg2 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->back_to_back_ipg > + MAX_INTER_PACKET_GAP)) { + pr_err("Inter packet gap can't be greater than %d\n", + MAX_INTER_PACKET_GAP); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_retransmit > + MAX_RETRANSMISSION) { + pr_err("maxRetransmission can't be greater than %d\n", + MAX_RETRANSMISSION); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_coll_window > + MAX_COLLISION_WINDOW) { + pr_err("collisionWindow can't be greater than %d\n", + MAX_COLLISION_WINDOW); + return -EINVAL; + /* If Auto negotiation process is disabled, need to set up the PHY + * using the MII Management Interface + */ + } + if (!dtsec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!dtsec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_1G_BAB_RX: + bit_mask = DTSEC_IMASK_BREN; + break; + case FM_MAC_EX_1G_RX_CTL: + bit_mask = DTSEC_IMASK_RXCEN; + break; + case FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GTSCEN; + break; + case FM_MAC_EX_1G_BAB_TX: + bit_mask = DTSEC_IMASK_BTEN; + break; + case FM_MAC_EX_1G_TX_CTL: + bit_mask = DTSEC_IMASK_TXCEN; + break; + case FM_MAC_EX_1G_TX_ERR: + bit_mask = DTSEC_IMASK_TXEEN; + break; + case FM_MAC_EX_1G_LATE_COL: + bit_mask = DTSEC_IMASK_LCEN; + break; + case FM_MAC_EX_1G_COL_RET_LMT: + bit_mask = DTSEC_IMASK_CRLEN; + break; + case FM_MAC_EX_1G_TX_FIFO_UNDRN: + bit_mask = DTSEC_IMASK_XFUNEN; + break; + case FM_MAC_EX_1G_MAG_PCKT: + bit_mask = DTSEC_IMASK_MAGEN; + break; + case FM_MAC_EX_1G_MII_MNG_RD_COMPLET: + bit_mask = DTSEC_IMASK_MMRDEN; + break; + case FM_MAC_EX_1G_MII_MNG_WR_COMPLET: + bit_mask = DTSEC_IMASK_MMWREN; + break; + case FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GRSCEN; + break; + case FM_MAC_EX_1G_DATA_ERR: + bit_mask = DTSEC_IMASK_TDPEEN; + break; + case FM_MAC_EX_1G_RX_MIB_CNT_OVFL: + bit_mask = DTSEC_IMASK_MSROEN; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static bool is_init_done(struct dtsec_cfg *dtsec_drv_params) +{ + /* Checks if dTSEC driver parameters were initialized */ + if (!dtsec_drv_params) + return true; + + return false; +} + +static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (is_init_done(dtsec->dtsec_drv_param)) + return 0; + + return (u16)ioread32be(®s->maxfrm); +} + +static void dtsec_isr(void *handle) +{ + struct fman_mac *dtsec = (struct fman_mac *)handle; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 event; + + /* do not handle MDIO events */ + event = ioread32be(®s->ievent) & + (u32)(~(DTSEC_IMASK_MMRDEN | DTSEC_IMASK_MMWREN)); + + event &= ioread32be(®s->imask); + + iowrite32be(event, ®s->ievent); + + if (event & DTSEC_IMASK_BREN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_RX); + if (event & DTSEC_IMASK_RXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_RX_CTL); + if (event & DTSEC_IMASK_GTSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET); + if (event & DTSEC_IMASK_BTEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_TX); + if (event & DTSEC_IMASK_TXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_CTL); + if (event & DTSEC_IMASK_TXEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_ERR); + if (event & DTSEC_IMASK_LCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_LATE_COL); + if (event & DTSEC_IMASK_CRLEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT); + if (event & DTSEC_IMASK_XFUNEN) { + /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */ + if (dtsec->fm_rev_info.major == 2) { + u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i; + /* a. Write 0x00E0_0C00 to DTSEC_ID + * This is a read only register + * b. Read and save the value of TPKT + */ + tpkt1 = ioread32be(®s->tpkt); + + /* c. Read the register at dTSEC address offset 0x32C */ + tmp_reg1 = ioread32be(®s->reserved02c0[27]); + + /* d. Compare bits [9:15] to bits [25:31] of the + * register at address offset 0x32C. + */ + if ((tmp_reg1 & 0x007F0000) != + (tmp_reg1 & 0x0000007F)) { + /* If they are not equal, save the value of + * this register and wait for at least + * MAXFRM*16 ns + */ + usleep_range((u32)(min + (dtsec_get_max_frame_length(dtsec) * + 16 / 1000, 1)), (u32) + (min(dtsec_get_max_frame_length + (dtsec) * 16 / 1000, 1) + 1)); + } + + /* e. Read and save TPKT again and read the register + * at dTSEC address offset 0x32C again + */ + tpkt2 = ioread32be(®s->tpkt); + tmp_reg2 = ioread32be(®s->reserved02c0[27]); + + /* f. Compare the value of TPKT saved in step b to + * value read in step e. Also compare bits [9:15] of + * the register at offset 0x32C saved in step d to the + * value of bits [9:15] saved in step e. If the two + * registers values are unchanged, then the transmit + * portion of the dTSEC controller is locked up and + * the user should proceed to the recover sequence. + */ + if ((tpkt1 == tpkt2) && ((tmp_reg1 & 0x007F0000) == + (tmp_reg2 & 0x007F0000))) { + /* recover sequence */ + + /* a.Write a 1 to RCTRL[GRS] */ + + iowrite32be(ioread32be(®s->rctrl) | + RCTRL_GRS, ®s->rctrl); + + /* b.Wait until IEVENT[GRSC]=1, or at least + * 100 us has elapsed. + */ + for (i = 0; i < 100; i++) { + if (ioread32be(®s->ievent) & + DTSEC_IMASK_GRSCEN) + break; + udelay(1); + } + if (ioread32be(®s->ievent) & + DTSEC_IMASK_GRSCEN) + iowrite32be(DTSEC_IMASK_GRSCEN, + ®s->ievent); + else + pr_debug("Rx lockup due to Tx lockup\n"); + + /* c.Write a 1 to bit n of FM_RSTC + * (offset 0x0CC of FPM) + */ + fman_reset_mac(dtsec->fm, dtsec->mac_id); + + /* d.Wait 4 Tx clocks (32 ns) */ + udelay(1); + + /* e.Write a 0 to bit n of FM_RSTC. */ + /* cleared by FMAN + */ + } + } + + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_FIFO_UNDRN); + } + if (event & DTSEC_IMASK_MAGEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_MAG_PCKT); + if (event & DTSEC_IMASK_GRSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET); + if (event & DTSEC_IMASK_TDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_DATA_ERR); + if (event & DTSEC_IMASK_RDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_1G_RX_DATA_ERR); + + /* masked interrupts */ + WARN_ON(event & DTSEC_IMASK_ABRTEN); + WARN_ON(event & DTSEC_IMASK_IFERREN); +} + +static void dtsec_1588_isr(void *handle) +{ + struct fman_mac *dtsec = (struct fman_mac *)handle; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 event; + + if (dtsec->ptp_tsu_enabled) { + event = ioread32be(®s->tmr_pevent); + event &= ioread32be(®s->tmr_pemask); + + if (event) { + iowrite32be(event, ®s->tmr_pevent); + WARN_ON(event & TMR_PEVENT_TSRE); + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_1588_TS_RX_ERR); + } + } +} + +static void free_init_resources(struct fman_mac *dtsec) +{ + fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_ERR); + fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(dtsec->multicast_addr_hash); + dtsec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(dtsec->unicast_addr_hash); + dtsec->unicast_addr_hash = NULL; +} + +int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->maximum_frame = new_val; + + return 0; +} + +int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->tx_pad_crc = new_val; + + return 0; +} + +static void graceful_start(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (mode & COMM_MODE_TX) + iowrite32be(ioread32be(®s->tctrl) & + ~TCTRL_GTS, ®s->tctrl); + if (mode & COMM_MODE_RX) + iowrite32be(ioread32be(®s->rctrl) & + ~RCTRL_GRS, ®s->rctrl); +} + +static void graceful_stop(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + /* Graceful stop - Assert the graceful Rx stop bit */ + if (mode & COMM_MODE_RX) { + tmp = ioread32be(®s->rctrl) | RCTRL_GRS; + iowrite32be(tmp, ®s->rctrl); + + if (dtsec->fm_rev_info.major == 2) { + /* Workaround for dTSEC Errata A002 */ + usleep_range(100, 200); + } else { + /* Workaround for dTSEC Errata A004839 */ + usleep_range(10, 50); + } + } + + /* Graceful stop - Assert the graceful Tx stop bit */ + if (mode & COMM_MODE_TX) { + if (dtsec->fm_rev_info.major == 2) { + /* dTSEC Errata A004: Do not use TCTRL[GTS]=1 */ + pr_debug("GTS not supported due to DTSEC_A004 Errata.\n"); + } else { + tmp = ioread32be(®s->tctrl) | TCTRL_GTS; + iowrite32be(tmp, ®s->tctrl); + + /* Workaround for dTSEC Errata A0012, A0014 */ + usleep_range(10, 50); + } + } +} + +int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Enable */ + tmp = ioread32be(®s->maccfg1); + if (mode & COMM_MODE_RX) + tmp |= MACCFG1_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= MACCFG1_TX_EN; + + iowrite32be(tmp, ®s->maccfg1); + + /* Graceful start - clear the graceful Rx/Tx stop bit */ + graceful_start(dtsec, mode); + + return 0; +} + +int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Graceful stop - Assert the graceful Rx/Tx stop bit */ + graceful_stop(dtsec, mode); + + tmp = ioread32be(®s->maccfg1); + if (mode & COMM_MODE_RX) + tmp &= ~MACCFG1_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~MACCFG1_TX_EN; + + iowrite32be(tmp, ®s->maccfg1); + + return 0; +} + +int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, + u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; + u32 ptv = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + + if (pause_time) { + /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ + if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) { + pr_warn("pause-time: %d illegal.Should be > 320\n", + pause_time); + return -EINVAL; + } + + ptv = ioread32be(®s->ptv); + ptv &= PTV_PTE_MASK; + ptv |= pause_time & PTV_PT_MASK; + iowrite32be(ptv, ®s->ptv); + + /* trigger the transmission of a flow-control pause frame */ + iowrite32be(ioread32be(®s->maccfg1) | MACCFG1_TX_FLOW, + ®s->maccfg1); + } else + iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, + ®s->maccfg1); + + graceful_start(dtsec, mode); + + return 0; +} + +int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + + tmp = ioread32be(®s->maccfg1); + if (en) + tmp |= MACCFG1_RX_FLOW; + else + tmp &= ~MACCFG1_RX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + + graceful_start(dtsec, mode); + + return 0; +} + +int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + + /* Initialize MAC Station Address registers (1 & 2) + * Station address have to be swapped (big endian to little endian + */ + dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); + set_mac_address(dtsec->regs, (u8 *)(*enet_addr)); + + graceful_start(dtsec, mode); + + return 0; +} + +int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct eth_hash_entry *hash_entry; + u64 addr; + s32 bucket; + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + + /* considering the 9 highest order bits in crc H[8:0]: + *if ghtx = 0 H[8:6] (highest order 3 bits) identify the hash register + *and H[5:1] (next 5 bits) identify the hash bit + *if ghts = 1 H[8:5] (highest order 4 bits) identify the hash register + *and H[4:0] (next 5 bits) identify the hash bit. + * + *In bucket index output the low 5 bits identify the hash register + *bit, while the higher 4 bits identify the hash register + */ + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set in gaddr instead of + *igaddr. + */ + if (mcast) + bucket += 0x100; + } + + set_bucket(dtsec->regs, bucket, true); + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_ATOMIC); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + if (addr & MAC_GROUP_ADDRESS) + /* Group Address */ + list_add_tail(&hash_entry->node, + &dtsec->multicast_addr_hash->lsts[bucket]); + else + list_add_tail(&hash_entry->node, + &dtsec->unicast_addr_hash->lsts[bucket]); + + return 0; +} + +int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable) +{ + u32 tmp; + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->rctrl); + if (enable) + tmp |= RCTRL_MPROM; + else + tmp &= ~RCTRL_MPROM; + + iowrite32be(tmp, ®s->rctrl); + + return 0; +} + +int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 rctrl, tctrl; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + rctrl = ioread32be(®s->rctrl); + tctrl = ioread32be(®s->tctrl); + + if (enable) { + rctrl |= RCTRL_RTSE; + tctrl |= TCTRL_TTSE; + } else { + rctrl &= ~RCTRL_RTSE; + tctrl &= ~TCTRL_TTSE; + } + + iowrite32be(rctrl, ®s->rctrl); + iowrite32be(tctrl, ®s->tctrl); + + return 0; +} + +int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct list_head *pos; + struct eth_hash_entry *hash_entry = NULL; + u64 addr; + s32 bucket; + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set + * in gaddr instead of igaddr. + */ + if (mcast) + bucket += 0x100; + } + + if (addr & MAC_GROUP_ADDRESS) { + /* Group Address */ + list_for_each(pos, + &dtsec->multicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry && hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->multicast_addr_hash->lsts[bucket])) + set_bucket(dtsec->regs, bucket, false); + } else { + /* Individual Address */ + list_for_each(pos, + &dtsec->unicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry && hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->unicast_addr_hash->lsts[bucket])) + set_bucket(dtsec->regs, bucket, false); + } + + /* address does not exist */ + WARN_ON(!hash_entry); + + return 0; +} + +int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Set unicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) + tmp |= RCTRL_UPROM; + else + tmp &= ~RCTRL_UPROM; + + iowrite32be(tmp, ®s->rctrl); + + /* Set multicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) + tmp |= RCTRL_MPROM; + else + tmp &= ~RCTRL_MPROM; + + iowrite32be(tmp, ®s->rctrl); + + return 0; +} + +int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + + tmp = ioread32be(®s->maccfg2); + + /* Full Duplex */ + tmp |= MACCFG2_FULL_DUPLEX; + + tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); + if (speed < SPEED_1000) + tmp |= MACCFG2_NIBBLE_MODE; + else if (speed == SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + iowrite32be(tmp, ®s->maccfg2); + + tmp = ioread32be(®s->ecntrl); + if (speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + else + tmp &= ~DTSEC_ECNTRL_R100M; + iowrite32be(tmp, ®s->ecntrl); + + graceful_start(dtsec, mode); + + return 0; +} + +int dtsec_restart_autoneg(struct fman_mac *dtsec) +{ + u16 tmp_reg16; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR); + + tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000); + tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART | + BMCR_FULLDPLX | BMCR_SPEED1000); + + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + + return 0; +} + +int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + *mac_version = ioread32be(®s->tsec_id); + + return 0; +} + +int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 bit_mask = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) { + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + dtsec->exceptions |= bit_mask; + else + dtsec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + iowrite32be(ioread32be(®s->imask) | bit_mask, + ®s->imask); + else + iowrite32be(ioread32be(®s->imask) & ~bit_mask, + ®s->imask); + } else { + if (!dtsec->ptp_tsu_enabled) { + pr_err("Exception valid for 1588 only\n"); + return -EINVAL; + } + switch (exception) { + case FM_MAC_EX_1G_1588_TS_RX_ERR: + if (enable) { + dtsec->en_tsu_err_exception = true; + iowrite32be(ioread32be(®s->tmr_pemask) | + TMR_PEMASK_TSREEN, + ®s->tmr_pemask); + } else { + dtsec->en_tsu_err_exception = false; + iowrite32be(ioread32be(®s->tmr_pemask) & + ~TMR_PEMASK_TSREEN, + ®s->tmr_pemask); + } + break; + default: + pr_err("Undefined exception\n"); + return -EINVAL; + } + } + + return 0; +} + +int dtsec_init(struct fman_mac *dtsec) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct dtsec_cfg *dtsec_drv_param; + u16 max_frm_ln; + int err; + + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(dtsec); + if (err) + return err; + + dtsec_drv_param = dtsec->dtsec_drv_param; + + err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if, + dtsec->max_speed, dtsec->addr, dtsec->exceptions, + dtsec->tbiphy->mdio.addr); + if (err) { + free_init_resources(dtsec); + pr_err("DTSEC version doesn't support this i/f mode\n"); + return err; + } + + if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) { + u16 tmp_reg16; + + /* Configure the TBI PHY Control Register */ + tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET; + phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); + + tmp_reg16 = TBICON_CLK_SELECT; + phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); + + tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE | + BMCR_FULLDPLX | BMCR_SPEED1000); + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + + if (dtsec->basex_if) + tmp_reg16 = TBIANA_1000X; + else + tmp_reg16 = TBIANA_SGMII; + phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16); + + tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART | + BMCR_FULLDPLX | BMCR_SPEED1000); + + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + } + + /* Max Frame Length */ + max_frm_ln = (u16)ioread32be(®s->maxfrm); + err = fman_set_mac_max_frame(dtsec->fm, dtsec->mac_id, max_frm_ln); + if (err) { + pr_err("Setting max frame length failed\n"); + free_init_resources(dtsec); + return -EINVAL; + } + + dtsec->multicast_addr_hash = + alloc_hash_table(EXTENDED_HASH_TABLE_SIZE); + if (!dtsec->multicast_addr_hash) { + free_init_resources(dtsec); + pr_err("MC hash table is failed\n"); + return -ENOMEM; + } + + dtsec->unicast_addr_hash = alloc_hash_table(DTSEC_HASH_TABLE_SIZE); + if (!dtsec->unicast_addr_hash) { + free_init_resources(dtsec); + pr_err("UC hash table is failed\n"); + return -ENOMEM; + } + + /* register err intr handler for dtsec to FPM (err) */ + fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_ERR, dtsec_isr, dtsec); + /* register 1588 intr handler for TMR to FPM (normal) */ + fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_NORMAL, dtsec_1588_isr, dtsec); + + kfree(dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + + return 0; +} + +int dtsec_free(struct fman_mac *dtsec) +{ + free_init_resources(dtsec); + + kfree(dtsec->dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + kfree(dtsec); + + return 0; +} + +struct fman_mac *dtsec_config(struct fman_mac_params *params) +{ + struct fman_mac *dtsec; + struct dtsec_cfg *dtsec_drv_param; + void __iomem *base_addr; + + base_addr = params->base_addr; + + /* allocate memory for the UCC GETH data structure. */ + dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL); + if (!dtsec) + return NULL; + + /* allocate memory for the d_tsec driver parameters data structure. */ + dtsec_drv_param = kzalloc(sizeof(*dtsec_drv_param), GFP_KERNEL); + if (!dtsec_drv_param) + goto err_dtsec; + + /* Plant parameter structure pointer */ + dtsec->dtsec_drv_param = dtsec_drv_param; + + set_dflts(dtsec_drv_param); + + dtsec->regs = base_addr; + dtsec->addr = ENET_ADDR_TO_UINT64(params->addr); + dtsec->max_speed = params->max_speed; + dtsec->phy_if = params->phy_if; + dtsec->mac_id = params->mac_id; + dtsec->exceptions = (DTSEC_IMASK_BREN | + DTSEC_IMASK_RXCEN | + DTSEC_IMASK_BTEN | + DTSEC_IMASK_TXCEN | + DTSEC_IMASK_TXEEN | + DTSEC_IMASK_ABRTEN | + DTSEC_IMASK_LCEN | + DTSEC_IMASK_CRLEN | + DTSEC_IMASK_XFUNEN | + DTSEC_IMASK_IFERREN | + DTSEC_IMASK_MAGEN | + DTSEC_IMASK_TDPEEN | + DTSEC_IMASK_RDPEEN); + dtsec->exception_cb = params->exception_cb; + dtsec->event_cb = params->event_cb; + dtsec->dev_id = params->dev_id; + dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en; + dtsec->en_tsu_err_exception = dtsec->dtsec_drv_param->ptp_exception_en; + + dtsec->fm = params->fm; + dtsec->basex_if = params->basex_if; + + if (!params->internal_phy_node) { + pr_err("TBI PHY node is not available\n"); + goto err_dtsec_drv_param; + } + + dtsec->tbiphy = of_phy_find_device(params->internal_phy_node); + if (!dtsec->tbiphy) { + pr_err("of_phy_find_device (TBI PHY) failed\n"); + goto err_dtsec_drv_param; + } + + put_device(&dtsec->tbiphy->mdio.dev); + + /* Save FMan revision */ + fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); + + return dtsec; + +err_dtsec_drv_param: + kfree(dtsec_drv_param); +err_dtsec: + kfree(dtsec); + return NULL; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h new file mode 100644 index 000000000..5149d96ec --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h @@ -0,0 +1,61 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __DTSEC_H +#define __DTSEC_H + +#include "fman_mac.h" + +struct fman_mac *dtsec_config(struct fman_mac_params *params); +int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val); +int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr); +int dtsec_adjust_link(struct fman_mac *dtsec, + u16 speed); +int dtsec_restart_autoneg(struct fman_mac *dtsec); +int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val); +int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val); +int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode); +int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode); +int dtsec_init(struct fman_mac *dtsec); +int dtsec_free(struct fman_mac *dtsec); +int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en); +int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u8 priority, + u16 pause_time, u16 thresh_time); +int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable); +int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); +int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); +int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version); +int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable); +int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable); + +#endif /* __DTSEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.c b/drivers/net/ethernet/freescale/fman/fman_keygen.c new file mode 100644 index 000000000..e1bdfed16 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.c @@ -0,0 +1,784 @@ +/* + * Copyright 2017 NXP + * + * 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 NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``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 NXP 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> + +#include "fman_keygen.h" + +/* Maximum number of HW Ports */ +#define FMAN_MAX_NUM_OF_HW_PORTS 64 + +/* Maximum number of KeyGen Schemes */ +#define FM_KG_MAX_NUM_OF_SCHEMES 32 + +/* Number of generic KeyGen Generic Extract Command Registers */ +#define FM_KG_NUM_OF_GENERIC_REGS 8 + +/* Dummy port ID */ +#define DUMMY_PORT_ID 0 + +/* Select Scheme Value Register */ +#define KG_SCH_DEF_USE_KGSE_DV_0 2 +#define KG_SCH_DEF_USE_KGSE_DV_1 3 + +/* Registers Shifting values */ +#define FM_KG_KGAR_NUM_SHIFT 16 +#define KG_SCH_DEF_L4_PORT_SHIFT 8 +#define KG_SCH_DEF_IP_ADDR_SHIFT 18 +#define KG_SCH_HASH_CONFIG_SHIFT_SHIFT 24 + +/* KeyGen Registers bit field masks: */ + +/* Enable bit field mask for KeyGen General Configuration Register */ +#define FM_KG_KGGCR_EN 0x80000000 + +/* KeyGen Global Registers bit field masks */ +#define FM_KG_KGAR_GO 0x80000000 +#define FM_KG_KGAR_READ 0x40000000 +#define FM_KG_KGAR_WRITE 0x00000000 +#define FM_KG_KGAR_SEL_SCHEME_ENTRY 0x00000000 +#define FM_KG_KGAR_SCM_WSEL_UPDATE_CNT 0x00008000 + +#define FM_KG_KGAR_ERR 0x20000000 +#define FM_KG_KGAR_SEL_CLS_PLAN_ENTRY 0x01000000 +#define FM_KG_KGAR_SEL_PORT_ENTRY 0x02000000 +#define FM_KG_KGAR_SEL_PORT_WSEL_SP 0x00008000 +#define FM_KG_KGAR_SEL_PORT_WSEL_CPP 0x00004000 + +/* Error events exceptions */ +#define FM_EX_KG_DOUBLE_ECC 0x80000000 +#define FM_EX_KG_KEYSIZE_OVERFLOW 0x40000000 + +/* Scheme Registers bit field masks */ +#define KG_SCH_MODE_EN 0x80000000 +#define KG_SCH_VSP_NO_KSP_EN 0x80000000 +#define KG_SCH_HASH_CONFIG_SYM 0x40000000 + +/* Known Protocol field codes */ +#define KG_SCH_KN_PORT_ID 0x80000000 +#define KG_SCH_KN_MACDST 0x40000000 +#define KG_SCH_KN_MACSRC 0x20000000 +#define KG_SCH_KN_TCI1 0x10000000 +#define KG_SCH_KN_TCI2 0x08000000 +#define KG_SCH_KN_ETYPE 0x04000000 +#define KG_SCH_KN_PPPSID 0x02000000 +#define KG_SCH_KN_PPPID 0x01000000 +#define KG_SCH_KN_MPLS1 0x00800000 +#define KG_SCH_KN_MPLS2 0x00400000 +#define KG_SCH_KN_MPLS_LAST 0x00200000 +#define KG_SCH_KN_IPSRC1 0x00100000 +#define KG_SCH_KN_IPDST1 0x00080000 +#define KG_SCH_KN_PTYPE1 0x00040000 +#define KG_SCH_KN_IPTOS_TC1 0x00020000 +#define KG_SCH_KN_IPV6FL1 0x00010000 +#define KG_SCH_KN_IPSRC2 0x00008000 +#define KG_SCH_KN_IPDST2 0x00004000 +#define KG_SCH_KN_PTYPE2 0x00002000 +#define KG_SCH_KN_IPTOS_TC2 0x00001000 +#define KG_SCH_KN_IPV6FL2 0x00000800 +#define KG_SCH_KN_GREPTYPE 0x00000400 +#define KG_SCH_KN_IPSEC_SPI 0x00000200 +#define KG_SCH_KN_IPSEC_NH 0x00000100 +#define KG_SCH_KN_IPPID 0x00000080 +#define KG_SCH_KN_L4PSRC 0x00000004 +#define KG_SCH_KN_L4PDST 0x00000002 +#define KG_SCH_KN_TFLG 0x00000001 + +/* NIA values */ +#define NIA_ENG_BMI 0x00500000 +#define NIA_BMI_AC_ENQ_FRAME 0x00000002 +#define ENQUEUE_KG_DFLT_NIA (NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME) + +/* Hard-coded configuration: + * These values are used as hard-coded values for KeyGen configuration + * and they replace user selections for this hard-coded version + */ + +/* Hash distribution shift */ +#define DEFAULT_HASH_DIST_FQID_SHIFT 0 + +/* Hash shift */ +#define DEFAULT_HASH_SHIFT 0 + +/* Symmetric hash usage: + * Warning: + * - the value for symmetric hash usage must be in accordance with hash + * key defined below + * - according to tests performed, spreading is not working if symmetric + * hash is set on true + * So ultimately symmetric hash functionality should be always disabled: + */ +#define DEFAULT_SYMMETRIC_HASH false + +/* Hash Key extraction fields: */ +#define DEFAULT_HASH_KEY_EXTRACT_FIELDS \ + (KG_SCH_KN_IPSRC1 | KG_SCH_KN_IPDST1 | \ + KG_SCH_KN_L4PSRC | KG_SCH_KN_L4PDST | \ + KG_SCH_KN_IPSEC_SPI) + +/* Default values to be used as hash key in case IPv4 or L4 (TCP, UDP) + * don't exist in the frame + */ +/* Default IPv4 address */ +#define DEFAULT_HASH_KEY_IPv4_ADDR 0x0A0A0A0A +/* Default L4 port */ +#define DEFAULT_HASH_KEY_L4_PORT 0x0B0B0B0B + +/* KeyGen Memory Mapped Registers: */ + +/* Scheme Configuration RAM Registers */ +struct fman_kg_scheme_regs { + u32 kgse_mode; /* 0x100: MODE */ + u32 kgse_ekfc; /* 0x104: Extract Known Fields Command */ + u32 kgse_ekdv; /* 0x108: Extract Known Default Value */ + u32 kgse_bmch; /* 0x10C: Bit Mask Command High */ + u32 kgse_bmcl; /* 0x110: Bit Mask Command Low */ + u32 kgse_fqb; /* 0x114: Frame Queue Base */ + u32 kgse_hc; /* 0x118: Hash Command */ + u32 kgse_ppc; /* 0x11C: Policer Profile Command */ + u32 kgse_gec[FM_KG_NUM_OF_GENERIC_REGS]; + /* 0x120: Generic Extract Command */ + u32 kgse_spc; + /* 0x140: KeyGen Scheme Entry Statistic Packet Counter */ + u32 kgse_dv0; /* 0x144: KeyGen Scheme Entry Default Value 0 */ + u32 kgse_dv1; /* 0x148: KeyGen Scheme Entry Default Value 1 */ + u32 kgse_ccbs; + /* 0x14C: KeyGen Scheme Entry Coarse Classification Bit*/ + u32 kgse_mv; /* 0x150: KeyGen Scheme Entry Match vector */ + u32 kgse_om; /* 0x154: KeyGen Scheme Entry Operation Mode bits */ + u32 kgse_vsp; + /* 0x158: KeyGen Scheme Entry Virtual Storage Profile */ +}; + +/* Port Partition Configuration Registers */ +struct fman_kg_pe_regs { + u32 fmkg_pe_sp; /* 0x100: KeyGen Port entry Scheme Partition */ + u32 fmkg_pe_cpp; + /* 0x104: KeyGen Port Entry Classification Plan Partition */ +}; + +/* General Configuration and Status Registers + * Global Statistic Counters + * KeyGen Global Registers + */ +struct fman_kg_regs { + u32 fmkg_gcr; /* 0x000: KeyGen General Configuration Register */ + u32 res004; /* 0x004: Reserved */ + u32 res008; /* 0x008: Reserved */ + u32 fmkg_eer; /* 0x00C: KeyGen Error Event Register */ + u32 fmkg_eeer; /* 0x010: KeyGen Error Event Enable Register */ + u32 res014; /* 0x014: Reserved */ + u32 res018; /* 0x018: Reserved */ + u32 fmkg_seer; /* 0x01C: KeyGen Scheme Error Event Register */ + u32 fmkg_seeer; /* 0x020: KeyGen Scheme Error Event Enable Register */ + u32 fmkg_gsr; /* 0x024: KeyGen Global Status Register */ + u32 fmkg_tpc; /* 0x028: Total Packet Counter Register */ + u32 fmkg_serc; /* 0x02C: Soft Error Capture Register */ + u32 res030[4]; /* 0x030: Reserved */ + u32 fmkg_fdor; /* 0x034: Frame Data Offset Register */ + u32 fmkg_gdv0r; /* 0x038: Global Default Value Register 0 */ + u32 fmkg_gdv1r; /* 0x03C: Global Default Value Register 1 */ + u32 res04c[6]; /* 0x040: Reserved */ + u32 fmkg_feer; /* 0x044: Force Error Event Register */ + u32 res068[38]; /* 0x048: Reserved */ + union { + u32 fmkg_indirect[63]; /* 0x100: Indirect Access Registers */ + struct fman_kg_scheme_regs fmkg_sch; /* Scheme Registers */ + struct fman_kg_pe_regs fmkg_pe; /* Port Partition Registers */ + }; + u32 fmkg_ar; /* 0x1FC: KeyGen Action Register */ +}; + +/* KeyGen Scheme data */ +struct keygen_scheme { + bool used; /* Specifies if this scheme is used */ + u8 hw_port_id; + /* Hardware port ID + * schemes sharing between multiple ports is not + * currently supported + * so we have only one port id bound to a scheme + */ + u32 base_fqid; + /* Base FQID: + * Must be between 1 and 2^24-1 + * If hash is used and an even distribution is + * expected according to hash_fqid_count, + * base_fqid must be aligned to hash_fqid_count + */ + u32 hash_fqid_count; + /* FQ range for hash distribution: + * Must be a power of 2 + * Represents the range of queues for spreading + */ + bool use_hashing; /* Usage of Hashing and spreading over FQ */ + bool symmetric_hash; /* Symmetric Hash option usage */ + u8 hashShift; + /* Hash result right shift. + * Select the 24 bits out of the 64 hash result. + * 0 means using the 24 LSB's, otherwise + * use the 24 LSB's after shifting right + */ + u32 match_vector; /* Match Vector */ +}; + +/* KeyGen driver data */ +struct fman_keygen { + struct keygen_scheme schemes[FM_KG_MAX_NUM_OF_SCHEMES]; + /* Array of schemes */ + struct fman_kg_regs __iomem *keygen_regs; /* KeyGen registers */ +}; + +/* keygen_write_ar_wait + * + * Write Action Register with specified value, wait for GO bit field to be + * idle and then read the error + * + * regs: KeyGen registers + * fmkg_ar: Action Register value + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_ar_wait(struct fman_kg_regs __iomem *regs, u32 fmkg_ar) +{ + iowrite32be(fmkg_ar, ®s->fmkg_ar); + + /* Wait for GO bit field to be idle */ + while (fmkg_ar & FM_KG_KGAR_GO) + fmkg_ar = ioread32be(®s->fmkg_ar); + + if (fmkg_ar & FM_KG_KGAR_ERR) + return -EINVAL; + + return 0; +} + +/* build_ar_scheme + * + * Build Action Register value for scheme settings + * + * scheme_id: Scheme ID + * update_counter: update scheme counter + * write: true for action to write the scheme or false for read action + * + * Return: AR value + */ +static u32 build_ar_scheme(u8 scheme_id, bool update_counter, bool write) +{ + u32 rw = (u32)(write ? FM_KG_KGAR_WRITE : FM_KG_KGAR_READ); + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_SCHEME_ENTRY | + DUMMY_PORT_ID | + ((u32)scheme_id << FM_KG_KGAR_NUM_SHIFT) | + (update_counter ? FM_KG_KGAR_SCM_WSEL_UPDATE_CNT : 0)); +} + +/* build_ar_bind_scheme + * + * Build Action Register value for port binding to schemes + * + * hwport_id: HW Port ID + * write: true for action to write the bind or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_scheme(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_SP); +} + +/* keygen_write_sp + * + * Write Scheme Partition Register with specified value + * + * regs: KeyGen Registers + * sp: Scheme Partition register value + * add: true to add a scheme partition or false to clear + * + * Return: none + */ +static void keygen_write_sp(struct fman_kg_regs __iomem *regs, u32 sp, bool add) +{ + u32 tmp; + + tmp = ioread32be(®s->fmkg_pe.fmkg_pe_sp); + + if (add) + tmp |= sp; + else + tmp &= ~sp; + + iowrite32be(tmp, ®s->fmkg_pe.fmkg_pe_sp); +} + +/* build_ar_bind_cls_plan + * + * Build Action Register value for Classification Plan + * + * hwport_id: HW Port ID + * write: true for action to write the CP or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_cls_plan(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_CPP); +} + +/* keygen_write_cpp + * + * Write Classification Plan Partition Register with specified value + * + * regs: KeyGen Registers + * cpp: CPP register value + * + * Return: none + */ +static void keygen_write_cpp(struct fman_kg_regs __iomem *regs, u32 cpp) +{ + iowrite32be(cpp, ®s->fmkg_pe.fmkg_pe_cpp); +} + +/* keygen_write_scheme + * + * Write all Schemes Registers with specified values + * + * regs: KeyGen Registers + * scheme_id: Scheme ID + * scheme_regs: Scheme registers values desired to be written + * update_counter: update scheme counter + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_scheme(struct fman_kg_regs __iomem *regs, u8 scheme_id, + struct fman_kg_scheme_regs *scheme_regs, + bool update_counter) +{ + u32 ar_reg; + int err, i; + + /* Write indirect scheme registers */ + iowrite32be(scheme_regs->kgse_mode, ®s->fmkg_sch.kgse_mode); + iowrite32be(scheme_regs->kgse_ekfc, ®s->fmkg_sch.kgse_ekfc); + iowrite32be(scheme_regs->kgse_ekdv, ®s->fmkg_sch.kgse_ekdv); + iowrite32be(scheme_regs->kgse_bmch, ®s->fmkg_sch.kgse_bmch); + iowrite32be(scheme_regs->kgse_bmcl, ®s->fmkg_sch.kgse_bmcl); + iowrite32be(scheme_regs->kgse_fqb, ®s->fmkg_sch.kgse_fqb); + iowrite32be(scheme_regs->kgse_hc, ®s->fmkg_sch.kgse_hc); + iowrite32be(scheme_regs->kgse_ppc, ®s->fmkg_sch.kgse_ppc); + iowrite32be(scheme_regs->kgse_spc, ®s->fmkg_sch.kgse_spc); + iowrite32be(scheme_regs->kgse_dv0, ®s->fmkg_sch.kgse_dv0); + iowrite32be(scheme_regs->kgse_dv1, ®s->fmkg_sch.kgse_dv1); + iowrite32be(scheme_regs->kgse_ccbs, ®s->fmkg_sch.kgse_ccbs); + iowrite32be(scheme_regs->kgse_mv, ®s->fmkg_sch.kgse_mv); + iowrite32be(scheme_regs->kgse_om, ®s->fmkg_sch.kgse_om); + iowrite32be(scheme_regs->kgse_vsp, ®s->fmkg_sch.kgse_vsp); + + for (i = 0 ; i < FM_KG_NUM_OF_GENERIC_REGS ; i++) + iowrite32be(scheme_regs->kgse_gec[i], + ®s->fmkg_sch.kgse_gec[i]); + + /* Write AR (Action register) */ + ar_reg = build_ar_scheme(scheme_id, update_counter, true); + err = keygen_write_ar_wait(regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return err; +} + +/* get_free_scheme_id + * + * Find the first free scheme available to be used + * + * keygen: KeyGen handle + * scheme_id: pointer to scheme id + * + * Return: 0 on success, -EINVAL when the are no available free schemes + */ +static int get_free_scheme_id(struct fman_keygen *keygen, u8 *scheme_id) +{ + u8 i; + + for (i = 0; i < FM_KG_MAX_NUM_OF_SCHEMES; i++) + if (!keygen->schemes[i].used) { + *scheme_id = i; + return 0; + } + + return -EINVAL; +} + +/* get_scheme + * + * Provides the scheme for specified ID + * + * keygen: KeyGen handle + * scheme_id: Scheme ID + * + * Return: handle to required scheme + */ +static struct keygen_scheme *get_scheme(struct fman_keygen *keygen, + u8 scheme_id) +{ + if (scheme_id >= FM_KG_MAX_NUM_OF_SCHEMES) + return NULL; + return &keygen->schemes[scheme_id]; +} + +/* keygen_bind_port_to_schemes + * + * Bind the port to schemes + * + * keygen: KeyGen handle + * scheme_id: id of the scheme to bind to + * bind: true to bind the port or false to unbind it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_bind_port_to_schemes(struct fman_keygen *keygen, + u8 scheme_id, + bool bind) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct keygen_scheme *scheme; + u32 ar_reg; + u32 schemes_vector = 0; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (!scheme->used) { + pr_err("Cannot bind port to an invalid scheme\n"); + return -EINVAL; + } + + schemes_vector |= 1 << (31 - scheme_id); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, false); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Reading Action Register failed\n"); + return err; + } + + keygen_write_sp(keygen_regs, schemes_vector, bind); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, true); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return 0; +} + +/* keygen_scheme_setup + * + * Setup the scheme according to required configuration + * + * keygen: KeyGen handle + * scheme_id: scheme ID + * enable: true to enable scheme or false to disable it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_scheme_setup(struct fman_keygen *keygen, u8 scheme_id, + bool enable) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct fman_kg_scheme_regs scheme_regs; + struct keygen_scheme *scheme; + u32 tmp_reg; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (enable && scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear scheme registers */ + memset(&scheme_regs, 0, sizeof(struct fman_kg_scheme_regs)); + + /* Setup all scheme registers: */ + tmp_reg = 0; + + if (enable) { + /* Enable Scheme */ + tmp_reg |= KG_SCH_MODE_EN; + /* Enqueue frame NIA */ + tmp_reg |= ENQUEUE_KG_DFLT_NIA; + } + + scheme_regs.kgse_mode = tmp_reg; + + scheme_regs.kgse_mv = scheme->match_vector; + + /* Scheme don't override StorageProfile: + * valid only for DPAA_VERSION >= 11 + */ + scheme_regs.kgse_vsp = KG_SCH_VSP_NO_KSP_EN; + + /* Configure Hard-Coded Rx Hashing: */ + + if (scheme->use_hashing) { + /* configure kgse_ekfc */ + scheme_regs.kgse_ekfc = DEFAULT_HASH_KEY_EXTRACT_FIELDS; + + /* configure kgse_ekdv */ + tmp_reg = 0; + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_0 << + KG_SCH_DEF_IP_ADDR_SHIFT); + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_1 << + KG_SCH_DEF_L4_PORT_SHIFT); + scheme_regs.kgse_ekdv = tmp_reg; + + /* configure kgse_dv0 */ + scheme_regs.kgse_dv0 = DEFAULT_HASH_KEY_IPv4_ADDR; + /* configure kgse_dv1 */ + scheme_regs.kgse_dv1 = DEFAULT_HASH_KEY_L4_PORT; + + /* configure kgse_hc */ + tmp_reg = 0; + tmp_reg |= ((scheme->hash_fqid_count - 1) << + DEFAULT_HASH_DIST_FQID_SHIFT); + tmp_reg |= scheme->hashShift << KG_SCH_HASH_CONFIG_SHIFT_SHIFT; + + if (scheme->symmetric_hash) { + /* Normally extraction key should be verified if + * complies with symmetric hash + * But because extraction is hard-coded, we are sure + * the key is symmetric + */ + tmp_reg |= KG_SCH_HASH_CONFIG_SYM; + } + scheme_regs.kgse_hc = tmp_reg; + } else { + scheme_regs.kgse_ekfc = 0; + scheme_regs.kgse_hc = 0; + scheme_regs.kgse_ekdv = 0; + scheme_regs.kgse_dv0 = 0; + scheme_regs.kgse_dv1 = 0; + } + + /* configure kgse_fqb: Scheme FQID base */ + tmp_reg = 0; + tmp_reg |= scheme->base_fqid; + scheme_regs.kgse_fqb = tmp_reg; + + /* features not used by hard-coded configuration */ + scheme_regs.kgse_bmch = 0; + scheme_regs.kgse_bmcl = 0; + scheme_regs.kgse_spc = 0; + + /* Write scheme registers */ + err = keygen_write_scheme(keygen_regs, scheme_id, &scheme_regs, true); + if (err != 0) { + pr_err("Writing scheme registers failed\n"); + return err; + } + + /* Update used field for Scheme */ + scheme->used = enable; + + return 0; +} + +/* keygen_init + * + * KeyGen initialization: + * Initializes and enables KeyGen, allocate driver memory, setup registers, + * clear port bindings, invalidate all schemes + * + * keygen_regs: KeyGen registers base address + * + * Return: Handle to KeyGen driver + */ +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs) +{ + struct fman_keygen *keygen; + u32 ar; + int i; + + /* Allocate memory for KeyGen driver */ + keygen = kzalloc(sizeof(*keygen), GFP_KERNEL); + if (!keygen) + return NULL; + + keygen->keygen_regs = keygen_regs; + + /* KeyGen initialization (for Master partition): + * Setup KeyGen registers + */ + iowrite32be(ENQUEUE_KG_DFLT_NIA, &keygen_regs->fmkg_gcr); + + iowrite32be(FM_EX_KG_DOUBLE_ECC | FM_EX_KG_KEYSIZE_OVERFLOW, + &keygen_regs->fmkg_eer); + + iowrite32be(0, &keygen_regs->fmkg_fdor); + iowrite32be(0, &keygen_regs->fmkg_gdv0r); + iowrite32be(0, &keygen_regs->fmkg_gdv1r); + + /* Clear binding between ports to schemes and classification plans + * so that all ports are not bound to any scheme/classification plan + */ + for (i = 0; i < FMAN_MAX_NUM_OF_HW_PORTS; i++) { + /* Clear all pe sp schemes registers */ + keygen_write_sp(keygen_regs, 0xffffffff, false); + ar = build_ar_bind_scheme(i, true); + keygen_write_ar_wait(keygen_regs, ar); + + /* Clear all pe cpp classification plans registers */ + keygen_write_cpp(keygen_regs, 0); + ar = build_ar_bind_cls_plan(i, true); + keygen_write_ar_wait(keygen_regs, ar); + } + + /* Enable all scheme interrupts */ + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seer); + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seeer); + + /* Enable KyeGen */ + iowrite32be(ioread32be(&keygen_regs->fmkg_gcr) | FM_KG_KGGCR_EN, + &keygen_regs->fmkg_gcr); + + return keygen; +} +EXPORT_SYMBOL(keygen_init); + +/* keygen_port_hashing_init + * + * Initializes a port for Rx Hashing with specified configuration parameters + * + * keygen: KeyGen handle + * hw_port_id: HW Port ID + * hash_base_fqid: Hashing Base FQID used for spreading + * hash_size: Hashing size + * + * Return: Zero for success or error code in case of failure + */ +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size) +{ + struct keygen_scheme *scheme; + u8 scheme_id; + int err; + + /* Validate Scheme configuration parameters */ + if (hash_base_fqid == 0 || (hash_base_fqid & ~0x00FFFFFF)) { + pr_err("Base FQID must be between 1 and 2^24-1\n"); + return -EINVAL; + } + if (hash_size == 0 || (hash_size & (hash_size - 1)) != 0) { + pr_err("Hash size must be power of two\n"); + return -EINVAL; + } + + /* Find a free scheme */ + err = get_free_scheme_id(keygen, &scheme_id); + if (err) { + pr_err("The maximum number of available Schemes has been exceeded\n"); + return -EINVAL; + } + + /* Create and configure Hard-Coded Scheme: */ + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear all scheme fields because the scheme may have been + * previously used + */ + memset(scheme, 0, sizeof(struct keygen_scheme)); + + /* Setup scheme: */ + scheme->hw_port_id = hw_port_id; + scheme->use_hashing = true; + scheme->base_fqid = hash_base_fqid; + scheme->hash_fqid_count = hash_size; + scheme->symmetric_hash = DEFAULT_SYMMETRIC_HASH; + scheme->hashShift = DEFAULT_HASH_SHIFT; + + /* All Schemes in hard-coded configuration + * are Indirect Schemes + */ + scheme->match_vector = 0; + + err = keygen_scheme_setup(keygen, scheme_id, true); + if (err != 0) { + pr_err("Scheme setup failed\n"); + return err; + } + + /* Bind Rx port to Scheme */ + err = keygen_bind_port_to_schemes(keygen, scheme_id, true); + if (err != 0) { + pr_err("Binding port to schemes failed\n"); + return err; + } + + return 0; +} +EXPORT_SYMBOL(keygen_port_hashing_init); diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.h b/drivers/net/ethernet/freescale/fman/fman_keygen.h new file mode 100644 index 000000000..c4640de3f --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 NXP + * + * 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 NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``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 NXP 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 __KEYGEN_H +#define __KEYGEN_H + +#include <linux/io.h> + +struct fman_keygen; +struct fman_kg_regs; + +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs); + +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size); + +#endif /* __KEYGEN_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h new file mode 100644 index 000000000..19f327efd --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_mac.h @@ -0,0 +1,274 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +/* FM MAC ... */ +#ifndef __FM_MAC_H +#define __FM_MAC_H + +#include "fman.h" + +#include <linux/slab.h> +#include <linux/phy.h> +#include <linux/if_ether.h> + +struct fman_mac; + +/* Ethernet Address */ +typedef u8 enet_addr_t[ETH_ALEN]; + +#define ENET_ADDR_TO_UINT64(_enet_addr) \ + (u64)(((u64)(_enet_addr)[0] << 40) | \ + ((u64)(_enet_addr)[1] << 32) | \ + ((u64)(_enet_addr)[2] << 24) | \ + ((u64)(_enet_addr)[3] << 16) | \ + ((u64)(_enet_addr)[4] << 8) | \ + ((u64)(_enet_addr)[5])) + +#define MAKE_ENET_ADDR_FROM_UINT64(_addr64, _enet_addr) \ + do { \ + int i; \ + for (i = 0; i < ETH_ALEN; i++) \ + (_enet_addr)[i] = \ + (u8)((_addr64) >> ((5 - i) * 8)); \ + } while (0) + +/* defaults */ +#define DEFAULT_RESET_ON_INIT false + +/* PFC defines */ +#define FSL_FM_PAUSE_TIME_ENABLE 0xf000 +#define FSL_FM_PAUSE_TIME_DISABLE 0 +#define FSL_FM_PAUSE_THRESH_DEFAULT 0 + +#define FM_MAC_NO_PFC 0xff + +/* HASH defines */ +#define ETH_HASH_ENTRY_OBJ(ptr) \ + hlist_entry_safe(ptr, struct eth_hash_entry, node) + +/* Enumeration (bit flags) of communication modes (Transmit, + * receive or both). + */ +enum comm_mode { + COMM_MODE_NONE = 0, /* No transmit/receive communication */ + COMM_MODE_RX = 1, /* Only receive communication */ + COMM_MODE_TX = 2, /* Only transmit communication */ + COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */ +}; + +/* FM MAC Exceptions */ +enum fman_mac_exceptions { + FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0 + /* 10GEC MDIO scan event interrupt */ + , FM_MAC_EX_10G_MDIO_CMD_CMPL + /* 10GEC MDIO command completion interrupt */ + , FM_MAC_EX_10G_REM_FAULT + /* 10GEC, mEMAC Remote fault interrupt */ + , FM_MAC_EX_10G_LOC_FAULT + /* 10GEC, mEMAC Local fault interrupt */ + , FM_MAC_EX_10G_TX_ECC_ER + /* 10GEC, mEMAC Transmit frame ECC error interrupt */ + , FM_MAC_EX_10G_TX_FIFO_UNFL + /* 10GEC, mEMAC Transmit FIFO underflow interrupt */ + , FM_MAC_EX_10G_TX_FIFO_OVFL + /* 10GEC, mEMAC Transmit FIFO overflow interrupt */ + , FM_MAC_EX_10G_TX_ER + /* 10GEC Transmit frame error interrupt */ + , FM_MAC_EX_10G_RX_FIFO_OVFL + /* 10GEC, mEMAC Receive FIFO overflow interrupt */ + , FM_MAC_EX_10G_RX_ECC_ER + /* 10GEC, mEMAC Receive frame ECC error interrupt */ + , FM_MAC_EX_10G_RX_JAB_FRM + /* 10GEC Receive jabber frame interrupt */ + , FM_MAC_EX_10G_RX_OVRSZ_FRM + /* 10GEC Receive oversized frame interrupt */ + , FM_MAC_EX_10G_RX_RUNT_FRM + /* 10GEC Receive runt frame interrupt */ + , FM_MAC_EX_10G_RX_FRAG_FRM + /* 10GEC Receive fragment frame interrupt */ + , FM_MAC_EX_10G_RX_LEN_ER + /* 10GEC Receive payload length error interrupt */ + , FM_MAC_EX_10G_RX_CRC_ER + /* 10GEC Receive CRC error interrupt */ + , FM_MAC_EX_10G_RX_ALIGN_ER + /* 10GEC Receive alignment error interrupt */ + , FM_MAC_EX_1G_BAB_RX + /* dTSEC Babbling receive error */ + , FM_MAC_EX_1G_RX_CTL + /* dTSEC Receive control (pause frame) interrupt */ + , FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET + /* dTSEC Graceful transmit stop complete */ + , FM_MAC_EX_1G_BAB_TX + /* dTSEC Babbling transmit error */ + , FM_MAC_EX_1G_TX_CTL + /* dTSEC Transmit control (pause frame) interrupt */ + , FM_MAC_EX_1G_TX_ERR + /* dTSEC Transmit error */ + , FM_MAC_EX_1G_LATE_COL + /* dTSEC Late collision */ + , FM_MAC_EX_1G_COL_RET_LMT + /* dTSEC Collision retry limit */ + , FM_MAC_EX_1G_TX_FIFO_UNDRN + /* dTSEC Transmit FIFO underrun */ + , FM_MAC_EX_1G_MAG_PCKT + /* dTSEC Magic Packet detection */ + , FM_MAC_EX_1G_MII_MNG_RD_COMPLET + /* dTSEC MII management read completion */ + , FM_MAC_EX_1G_MII_MNG_WR_COMPLET + /* dTSEC MII management write completion */ + , FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET + /* dTSEC Graceful receive stop complete */ + , FM_MAC_EX_1G_DATA_ERR + /* dTSEC Internal data error on transmit */ + , FM_MAC_1G_RX_DATA_ERR + /* dTSEC Internal data error on receive */ + , FM_MAC_EX_1G_1588_TS_RX_ERR + /* dTSEC Time-Stamp Receive Error */ + , FM_MAC_EX_1G_RX_MIB_CNT_OVFL + /* dTSEC MIB counter overflow */ + , FM_MAC_EX_TS_FIFO_ECC_ERR + /* mEMAC Time-stamp FIFO ECC error interrupt; + * not supported on T4240/B4860 rev1 chips + */ + , FM_MAC_EX_MAGIC_PACKET_INDICATION = FM_MAC_EX_1G_MAG_PCKT + /* mEMAC Magic Packet Indication Interrupt */ +}; + +struct eth_hash_entry { + u64 addr; /* Ethernet Address */ + struct list_head node; +}; + +typedef void (fman_mac_exception_cb)(void *dev_id, + enum fman_mac_exceptions exceptions); + +/* FMan MAC config input */ +struct fman_mac_params { + /* Base of memory mapped FM MAC registers */ + void __iomem *base_addr; + /* MAC address of device; First octet is sent first */ + enet_addr_t addr; + /* MAC ID; numbering of dTSEC and 1G-mEMAC: + * 0 - FM_MAX_NUM_OF_1G_MACS; + * numbering of 10G-MAC (TGEC) and 10G-mEMAC: + * 0 - FM_MAX_NUM_OF_10G_MACS + */ + u8 mac_id; + /* PHY interface */ + phy_interface_t phy_if; + /* Note that the speed should indicate the maximum rate that + * this MAC should support rather than the actual speed; + */ + u16 max_speed; + /* A handle to the FM object this port related to */ + void *fm; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ + fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */ + /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC + * and phy or backplane; Note: 1000BaseX auto-negotiation relates only + * to interface between MAC and phy/backplane, SGMII phy can still + * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps + */ + bool basex_if; + /* Pointer to TBI/PCS PHY node, used for TBI/PCS PHY access */ + struct device_node *internal_phy_node; +}; + +struct eth_hash_t { + u16 size; + struct list_head *lsts; +}; + +static inline struct eth_hash_entry +*dequeue_addr_from_hash_entry(struct list_head *addr_lst) +{ + struct eth_hash_entry *hash_entry = NULL; + + if (!list_empty(addr_lst)) { + hash_entry = ETH_HASH_ENTRY_OBJ(addr_lst->next); + list_del_init(&hash_entry->node); + } + return hash_entry; +} + +static inline void free_hash_table(struct eth_hash_t *hash) +{ + struct eth_hash_entry *hash_entry; + int i = 0; + + if (hash) { + if (hash->lsts) { + for (i = 0; i < hash->size; i++) { + hash_entry = + dequeue_addr_from_hash_entry(&hash->lsts[i]); + while (hash_entry) { + kfree(hash_entry); + hash_entry = + dequeue_addr_from_hash_entry(&hash-> + lsts[i]); + } + } + + kfree(hash->lsts); + } + + kfree(hash); + } +} + +static inline struct eth_hash_t *alloc_hash_table(u16 size) +{ + u32 i; + struct eth_hash_t *hash; + + /* Allocate address hash table */ + hash = kmalloc(sizeof(*hash), GFP_KERNEL); + if (!hash) + return NULL; + + hash->size = size; + + hash->lsts = kmalloc_array(hash->size, sizeof(struct list_head), + GFP_KERNEL); + if (!hash->lsts) { + kfree(hash); + return NULL; + } + + for (i = 0; i < hash->size; i++) + INIT_LIST_HEAD(&hash->lsts[i]); + + return hash; +} + +#endif /* __FM_MAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c new file mode 100644 index 000000000..bb9887f98 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -0,0 +1,1211 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_memac.h" +#include "fman.h" + +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/phy.h> +#include <linux/phy_fixed.h> +#include <linux/of_mdio.h> + +/* PCS registers */ +#define MDIO_SGMII_CR 0x00 +#define MDIO_SGMII_DEV_ABIL_SGMII 0x04 +#define MDIO_SGMII_LINK_TMR_L 0x12 +#define MDIO_SGMII_LINK_TMR_H 0x13 +#define MDIO_SGMII_IF_MODE 0x14 + +/* SGMII Control defines */ +#define SGMII_CR_AN_EN 0x1000 +#define SGMII_CR_RESTART_AN 0x0200 +#define SGMII_CR_FD 0x0100 +#define SGMII_CR_SPEED_SEL1_1G 0x0040 +#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \ + SGMII_CR_SPEED_SEL1_1G) + +/* SGMII Device Ability for SGMII defines */ +#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001 +#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0 + +/* Link timer define */ +#define LINK_TMR_L 0xa120 +#define LINK_TMR_H 0x0007 +#define LINK_TMR_L_BASEX 0xaf08 +#define LINK_TMR_H_BASEX 0x002f + +/* SGMII IF Mode defines */ +#define IF_MODE_USE_SGMII_AN 0x0002 +#define IF_MODE_SGMII_EN 0x0001 +#define IF_MODE_SGMII_SPEED_100M 0x0004 +#define IF_MODE_SGMII_SPEED_1G 0x0008 +#define IF_MODE_SGMII_DUPLEX_HALF 0x0010 + +/* Num of additional exact match MAC adr regs */ +#define MEMAC_NUM_OF_PADDRS 7 + +/* Control and Configuration Register (COMMAND_CONFIG) */ +#define CMD_CFG_REG_LOWP_RXETY 0x01000000 /* 07 Rx low power indication */ +#define CMD_CFG_TX_LOWP_ENA 0x00800000 /* 08 Tx Low Power Idle Enable */ +#define CMD_CFG_PFC_MODE 0x00080000 /* 12 Enable PFC */ +#define CMD_CFG_NO_LEN_CHK 0x00020000 /* 14 Payload length check disable */ +#define CMD_CFG_SW_RESET 0x00001000 /* 19 S/W Reset, self clearing bit */ +#define CMD_CFG_TX_PAD_EN 0x00000800 /* 20 Enable Tx padding of frames */ +#define CMD_CFG_PAUSE_IGNORE 0x00000100 /* 23 Ignore Pause frame quanta */ +#define CMD_CFG_CRC_FWD 0x00000040 /* 25 Terminate/frwd CRC of frames */ +#define CMD_CFG_PAD_EN 0x00000020 /* 26 Frame padding removal */ +#define CMD_CFG_PROMIS_EN 0x00000010 /* 27 Promiscuous operation enable */ +#define CMD_CFG_RX_EN 0x00000002 /* 30 MAC receive path enable */ +#define CMD_CFG_TX_EN 0x00000001 /* 31 MAC transmit path enable */ + +/* Transmit FIFO Sections Register (TX_FIFO_SECTIONS) */ +#define TX_FIFO_SECTIONS_TX_EMPTY_MASK 0xFFFF0000 +#define TX_FIFO_SECTIONS_TX_AVAIL_MASK 0x0000FFFF +#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G 0x00400000 +#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G 0x00100000 +#define TX_FIFO_SECTIONS_TX_AVAIL_10G 0x00000019 +#define TX_FIFO_SECTIONS_TX_AVAIL_1G 0x00000020 +#define TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G 0x00000060 + +#define GET_TX_EMPTY_DEFAULT_VALUE(_val) \ +do { \ + _val &= ~TX_FIFO_SECTIONS_TX_EMPTY_MASK; \ + ((_val == TX_FIFO_SECTIONS_TX_AVAIL_10G) ? \ + (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G) :\ + (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G));\ +} while (0) + +/* Interface Mode Register (IF_MODE) */ + +#define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */ +#define IF_MODE_10G 0x00000000 /* 30-31 10G interface */ +#define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */ +#define IF_MODE_RGMII 0x00000004 +#define IF_MODE_RGMII_AUTO 0x00008000 +#define IF_MODE_RGMII_1000 0x00004000 /* 10 - 1000Mbps RGMII */ +#define IF_MODE_RGMII_100 0x00000000 /* 00 - 100Mbps RGMII */ +#define IF_MODE_RGMII_10 0x00002000 /* 01 - 10Mbps RGMII */ +#define IF_MODE_RGMII_SP_MASK 0x00006000 /* Setsp mask bits */ +#define IF_MODE_RGMII_FD 0x00001000 /* Full duplex RGMII */ +#define IF_MODE_HD 0x00000040 /* Half duplex operation */ + +/* Hash table Control Register (HASHTABLE_CTRL) */ +#define HASH_CTRL_MCAST_EN 0x00000100 +/* 26-31 Hash table address code */ +#define HASH_CTRL_ADDR_MASK 0x0000003F +/* MAC mcast indication */ +#define GROUP_ADDRESS 0x0000010000000000LL +#define HASH_TABLE_SIZE 64 /* Hash tbl size */ + +/* Interrupt Mask Register (IMASK) */ +#define MEMAC_IMASK_MGI 0x40000000 /* 1 Magic pkt detect indication */ +#define MEMAC_IMASK_TSECC_ER 0x20000000 /* 2 Timestamp FIFO ECC error evnt */ +#define MEMAC_IMASK_TECC_ER 0x02000000 /* 6 Transmit frame ECC error evnt */ +#define MEMAC_IMASK_RECC_ER 0x01000000 /* 7 Receive frame ECC error evnt */ + +#define MEMAC_ALL_ERRS_IMASK \ + ((u32)(MEMAC_IMASK_TSECC_ER | \ + MEMAC_IMASK_TECC_ER | \ + MEMAC_IMASK_RECC_ER | \ + MEMAC_IMASK_MGI)) + +#define MEMAC_IEVNT_PCS 0x80000000 /* PCS (XG). Link sync (G) */ +#define MEMAC_IEVNT_AN 0x40000000 /* Auto-negotiation */ +#define MEMAC_IEVNT_LT 0x20000000 /* Link Training/New page */ +#define MEMAC_IEVNT_MGI 0x00004000 /* Magic pkt detection */ +#define MEMAC_IEVNT_TS_ECC_ER 0x00002000 /* Timestamp FIFO ECC error*/ +#define MEMAC_IEVNT_RX_FIFO_OVFL 0x00001000 /* Rx FIFO overflow */ +#define MEMAC_IEVNT_TX_FIFO_UNFL 0x00000800 /* Tx FIFO underflow */ +#define MEMAC_IEVNT_TX_FIFO_OVFL 0x00000400 /* Tx FIFO overflow */ +#define MEMAC_IEVNT_TX_ECC_ER 0x00000200 /* Tx frame ECC error */ +#define MEMAC_IEVNT_RX_ECC_ER 0x00000100 /* Rx frame ECC error */ +#define MEMAC_IEVNT_LI_FAULT 0x00000080 /* Link Interruption flt */ +#define MEMAC_IEVNT_RX_EMPTY 0x00000040 /* Rx FIFO empty */ +#define MEMAC_IEVNT_TX_EMPTY 0x00000020 /* Tx FIFO empty */ +#define MEMAC_IEVNT_RX_LOWP 0x00000010 /* Low Power Idle */ +#define MEMAC_IEVNT_PHY_LOS 0x00000004 /* Phy loss of signal */ +#define MEMAC_IEVNT_REM_FAULT 0x00000002 /* Remote fault (XGMII) */ +#define MEMAC_IEVNT_LOC_FAULT 0x00000001 /* Local fault (XGMII) */ + +#define DEFAULT_PAUSE_QUANTA 0xf000 +#define DEFAULT_FRAME_LENGTH 0x600 +#define DEFAULT_TX_IPG_LENGTH 12 + +#define CLXY_PAUSE_QUANTA_CLX_PQNT 0x0000FFFF +#define CLXY_PAUSE_QUANTA_CLY_PQNT 0xFFFF0000 +#define CLXY_PAUSE_THRESH_CLX_QTH 0x0000FFFF +#define CLXY_PAUSE_THRESH_CLY_QTH 0xFFFF0000 + +struct mac_addr { + /* Lower 32 bits of 48-bit MAC address */ + u32 mac_addr_l; + /* Upper 16 bits of 48-bit MAC address */ + u32 mac_addr_u; +}; + +/* memory map */ +struct memac_regs { + u32 res0000[2]; /* General Control and Status */ + u32 command_config; /* 0x008 Ctrl and cfg */ + struct mac_addr mac_addr0; /* 0x00C-0x010 MAC_ADDR_0...1 */ + u32 maxfrm; /* 0x014 Max frame length */ + u32 res0018[1]; + u32 rx_fifo_sections; /* Receive FIFO configuration reg */ + u32 tx_fifo_sections; /* Transmit FIFO configuration reg */ + u32 res0024[2]; + u32 hashtable_ctrl; /* 0x02C Hash table control */ + u32 res0030[4]; + u32 ievent; /* 0x040 Interrupt event */ + u32 tx_ipg_length; /* 0x044 Transmitter inter-packet-gap */ + u32 res0048; + u32 imask; /* 0x04C Interrupt mask */ + u32 res0050; + u32 pause_quanta[4]; /* 0x054 Pause quanta */ + u32 pause_thresh[4]; /* 0x064 Pause quanta threshold */ + u32 rx_pause_status; /* 0x074 Receive pause status */ + u32 res0078[2]; + struct mac_addr mac_addr[MEMAC_NUM_OF_PADDRS];/* 0x80-0x0B4 mac padr */ + u32 lpwake_timer; /* 0x0B8 Low Power Wakeup Timer */ + u32 sleep_timer; /* 0x0BC Transmit EEE Low Power Timer */ + u32 res00c0[8]; + u32 statn_config; /* 0x0E0 Statistics configuration */ + u32 res00e4[7]; + /* Rx Statistics Counter */ + u32 reoct_l; + u32 reoct_u; + u32 roct_l; + u32 roct_u; + u32 raln_l; + u32 raln_u; + u32 rxpf_l; + u32 rxpf_u; + u32 rfrm_l; + u32 rfrm_u; + u32 rfcs_l; + u32 rfcs_u; + u32 rvlan_l; + u32 rvlan_u; + u32 rerr_l; + u32 rerr_u; + u32 ruca_l; + u32 ruca_u; + u32 rmca_l; + u32 rmca_u; + u32 rbca_l; + u32 rbca_u; + u32 rdrp_l; + u32 rdrp_u; + u32 rpkt_l; + u32 rpkt_u; + u32 rund_l; + u32 rund_u; + u32 r64_l; + u32 r64_u; + u32 r127_l; + u32 r127_u; + u32 r255_l; + u32 r255_u; + u32 r511_l; + u32 r511_u; + u32 r1023_l; + u32 r1023_u; + u32 r1518_l; + u32 r1518_u; + u32 r1519x_l; + u32 r1519x_u; + u32 rovr_l; + u32 rovr_u; + u32 rjbr_l; + u32 rjbr_u; + u32 rfrg_l; + u32 rfrg_u; + u32 rcnp_l; + u32 rcnp_u; + u32 rdrntp_l; + u32 rdrntp_u; + u32 res01d0[12]; + /* Tx Statistics Counter */ + u32 teoct_l; + u32 teoct_u; + u32 toct_l; + u32 toct_u; + u32 res0210[2]; + u32 txpf_l; + u32 txpf_u; + u32 tfrm_l; + u32 tfrm_u; + u32 tfcs_l; + u32 tfcs_u; + u32 tvlan_l; + u32 tvlan_u; + u32 terr_l; + u32 terr_u; + u32 tuca_l; + u32 tuca_u; + u32 tmca_l; + u32 tmca_u; + u32 tbca_l; + u32 tbca_u; + u32 res0258[2]; + u32 tpkt_l; + u32 tpkt_u; + u32 tund_l; + u32 tund_u; + u32 t64_l; + u32 t64_u; + u32 t127_l; + u32 t127_u; + u32 t255_l; + u32 t255_u; + u32 t511_l; + u32 t511_u; + u32 t1023_l; + u32 t1023_u; + u32 t1518_l; + u32 t1518_u; + u32 t1519x_l; + u32 t1519x_u; + u32 res02a8[6]; + u32 tcnp_l; + u32 tcnp_u; + u32 res02c8[14]; + /* Line Interface Control */ + u32 if_mode; /* 0x300 Interface Mode Control */ + u32 if_status; /* 0x304 Interface Status */ + u32 res0308[14]; + /* HiGig/2 */ + u32 hg_config; /* 0x340 Control and cfg */ + u32 res0344[3]; + u32 hg_pause_quanta; /* 0x350 Pause quanta */ + u32 res0354[3]; + u32 hg_pause_thresh; /* 0x360 Pause quanta threshold */ + u32 res0364[3]; + u32 hgrx_pause_status; /* 0x370 Receive pause status */ + u32 hg_fifos_status; /* 0x374 fifos status */ + u32 rhm; /* 0x378 rx messages counter */ + u32 thm; /* 0x37C tx messages counter */ +}; + +struct memac_cfg { + bool reset_on_init; + bool pause_ignore; + bool promiscuous_mode_enable; + struct fixed_phy_status *fixed_link; + u16 max_frame_length; + u16 pause_quanta; + u32 tx_ipg_length; +}; + +struct fman_mac { + /* Pointer to MAC memory mapped registers */ + struct memac_regs __iomem *regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + phy_interface_t phy_if; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* Pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* Pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + struct memac_cfg *memac_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy_device *pcsphy; + bool allmulti_enabled; +}; + +static void add_addr_in_paddr(struct memac_regs __iomem *regs, u8 *adr, + u8 paddr_num) +{ + u32 tmp0, tmp1; + + tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); + tmp1 = (u32)(adr[4] | adr[5] << 8); + + if (paddr_num == 0) { + iowrite32be(tmp0, ®s->mac_addr0.mac_addr_l); + iowrite32be(tmp1, ®s->mac_addr0.mac_addr_u); + } else { + iowrite32be(tmp0, ®s->mac_addr[paddr_num - 1].mac_addr_l); + iowrite32be(tmp1, ®s->mac_addr[paddr_num - 1].mac_addr_u); + } +} + +static int reset(struct memac_regs __iomem *regs) +{ + u32 tmp; + int count; + + tmp = ioread32be(®s->command_config); + + tmp |= CMD_CFG_SW_RESET; + + iowrite32be(tmp, ®s->command_config); + + count = 100; + do { + udelay(1); + } while ((ioread32be(®s->command_config) & CMD_CFG_SW_RESET) && + --count); + + if (count == 0) + return -EBUSY; + + return 0; +} + +static void set_exception(struct memac_regs __iomem *regs, u32 val, + bool enable) +{ + u32 tmp; + + tmp = ioread32be(®s->imask); + if (enable) + tmp |= val; + else + tmp &= ~val; + + iowrite32be(tmp, ®s->imask); +} + +static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, + phy_interface_t phy_if, u16 speed, bool slow_10g_if, + u32 exceptions) +{ + u32 tmp; + + /* Config */ + tmp = 0; + if (cfg->promiscuous_mode_enable) + tmp |= CMD_CFG_PROMIS_EN; + if (cfg->pause_ignore) + tmp |= CMD_CFG_PAUSE_IGNORE; + + /* Payload length check disable */ + tmp |= CMD_CFG_NO_LEN_CHK; + /* Enable padding of frames in transmit direction */ + tmp |= CMD_CFG_TX_PAD_EN; + + tmp |= CMD_CFG_CRC_FWD; + + iowrite32be(tmp, ®s->command_config); + + /* Max Frame Length */ + iowrite32be((u32)cfg->max_frame_length, ®s->maxfrm); + + /* Pause Time */ + iowrite32be((u32)cfg->pause_quanta, ®s->pause_quanta[0]); + iowrite32be((u32)0, ®s->pause_thresh[0]); + + /* IF_MODE */ + tmp = 0; + switch (phy_if) { + case PHY_INTERFACE_MODE_XGMII: + tmp |= IF_MODE_10G; + break; + default: + tmp |= IF_MODE_GMII; + if (phy_if == PHY_INTERFACE_MODE_RGMII || + phy_if == PHY_INTERFACE_MODE_RGMII_ID || + phy_if == PHY_INTERFACE_MODE_RGMII_RXID || + phy_if == PHY_INTERFACE_MODE_RGMII_TXID) + tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO; + } + iowrite32be(tmp, ®s->if_mode); + + /* TX_FIFO_SECTIONS */ + tmp = 0; + if (phy_if == PHY_INTERFACE_MODE_XGMII) { + if (slow_10g_if) { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); + } else { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); + } + } else { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G); + } + iowrite32be(tmp, ®s->tx_fifo_sections); + + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + set_exception(regs, exceptions, true); + + return 0; +} + +static void set_dflts(struct memac_cfg *cfg) +{ + cfg->reset_on_init = false; + cfg->promiscuous_mode_enable = false; + cfg->pause_ignore = false; + cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH; + cfg->max_frame_length = DEFAULT_FRAME_LENGTH; + cfg->pause_quanta = DEFAULT_PAUSE_QUANTA; +} + +static u32 get_mac_addr_hash_code(u64 eth_addr) +{ + u64 mask1, mask2; + u32 xor_val = 0; + u8 i, j; + + for (i = 0; i < 6; i++) { + mask1 = eth_addr & (u64)0x01; + eth_addr >>= 1; + + for (j = 0; j < 7; j++) { + mask2 = eth_addr & (u64)0x01; + mask1 ^= mask2; + eth_addr >>= 1; + } + + xor_val |= (mask1 << (5 - i)); + } + + return xor_val; +} + +static void setup_sgmii_internal_phy(struct fman_mac *memac, + struct fixed_phy_status *fixed_link) +{ + u16 tmp_reg16; + + if (WARN_ON(!memac->pcsphy)) + return; + + /* SGMII mode */ + tmp_reg16 = IF_MODE_SGMII_EN; + if (!fixed_link) + /* AN enable */ + tmp_reg16 |= IF_MODE_USE_SGMII_AN; + else { + switch (fixed_link->speed) { + case 10: + /* For 10M: IF_MODE[SPEED_10M] = 0 */ + break; + case 100: + tmp_reg16 |= IF_MODE_SGMII_SPEED_100M; + break; + case 1000: + default: + tmp_reg16 |= IF_MODE_SGMII_SPEED_1G; + break; + } + if (!fixed_link->duplex) + tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF; + } + phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16); + + /* Device ability according to SGMII specification */ + tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE; + phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); + + /* Adjust link timer for SGMII - + * According to Cisco SGMII specification the timer should be 1.6 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H); + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L); + + if (!fixed_link) + /* Restart AN */ + tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + else + /* AN disabled */ + tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN; + phy_write(memac->pcsphy, 0x0, tmp_reg16); +} + +static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac) +{ + u16 tmp_reg16; + + /* AN Device capability */ + tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE; + phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); + + /* Adjust link timer for SGMII - + * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX); + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX); + + /* Restart AN */ + tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + phy_write(memac->pcsphy, 0x0, tmp_reg16); +} + +static int check_init_parameters(struct fman_mac *memac) +{ + if (!memac->exception_cb) { + pr_err("Uninitialized exception handler\n"); + return -EINVAL; + } + if (!memac->event_cb) { + pr_warn("Uninitialize event handler\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = MEMAC_IMASK_TECC_ER; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = MEMAC_IMASK_RECC_ER; + break; + case FM_MAC_EX_TS_FIFO_ECC_ERR: + bit_mask = MEMAC_IMASK_TSECC_ER; + break; + case FM_MAC_EX_MAGIC_PACKET_INDICATION: + bit_mask = MEMAC_IMASK_MGI; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void memac_err_exception(void *handle) +{ + struct fman_mac *memac = (struct fman_mac *)handle; + struct memac_regs __iomem *regs = memac->regs; + u32 event, imask; + + event = ioread32be(®s->ievent); + imask = ioread32be(®s->imask); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + iowrite32be(event, ®s->ievent); + + if (event & MEMAC_IEVNT_TS_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_TS_FIFO_ECC_ERR); + if (event & MEMAC_IEVNT_TX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & MEMAC_IEVNT_RX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_RX_ECC_ER); +} + +static void memac_exception(void *handle) +{ + struct fman_mac *memac = (struct fman_mac *)handle; + struct memac_regs __iomem *regs = memac->regs; + u32 event, imask; + + event = ioread32be(®s->ievent); + imask = ioread32be(®s->imask); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + iowrite32be(event, ®s->ievent); + + if (event & MEMAC_IEVNT_MGI) + memac->exception_cb(memac->dev_id, + FM_MAC_EX_MAGIC_PACKET_INDICATION); +} + +static void free_init_resources(struct fman_mac *memac) +{ + fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_ERR); + + fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(memac->multicast_addr_hash); + memac->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(memac->unicast_addr_hash); + memac->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct memac_cfg *memac_drv_params) +{ + /* Checks if mEMAC driver parameters were initialized */ + if (!memac_drv_params) + return true; + + return false; +} + +int memac_enable(struct fman_mac *memac, enum comm_mode mode) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp |= CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= CMD_CFG_TX_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_disable(struct fman_mac *memac, enum comm_mode mode) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp &= ~CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~CMD_CFG_TX_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_set_promiscuous(struct fman_mac *memac, bool new_val) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; + else + tmp &= ~CMD_CFG_PROMIS_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_adjust_link(struct fman_mac *memac, u16 speed) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->if_mode); + + /* Set full duplex */ + tmp &= ~IF_MODE_HD; + + if (phy_interface_mode_is_rgmii(memac->phy_if)) { + /* Configure RGMII in manual mode */ + tmp &= ~IF_MODE_RGMII_AUTO; + tmp &= ~IF_MODE_RGMII_SP_MASK; + /* Full duplex */ + tmp |= IF_MODE_RGMII_FD; + + switch (speed) { + case SPEED_1000: + tmp |= IF_MODE_RGMII_1000; + break; + case SPEED_100: + tmp |= IF_MODE_RGMII_100; + break; + case SPEED_10: + tmp |= IF_MODE_RGMII_10; + break; + default: + break; + } + } + + iowrite32be(tmp, ®s->if_mode); + + return 0; +} + +int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->max_frame_length = new_val; + + return 0; +} + +int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->reset_on_init = enable; + + return 0; +} + +int memac_cfg_fixed_link(struct fman_mac *memac, + struct fixed_phy_status *fixed_link) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->fixed_link = fixed_link; + + return 0; +} + +int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->tx_fifo_sections); + + GET_TX_EMPTY_DEFAULT_VALUE(tmp); + iowrite32be(tmp, ®s->tx_fifo_sections); + + tmp = ioread32be(®s->command_config); + tmp &= ~CMD_CFG_PFC_MODE; + + iowrite32be(tmp, ®s->command_config); + + tmp = ioread32be(®s->pause_quanta[priority / 2]); + if (priority % 2) + tmp &= CLXY_PAUSE_QUANTA_CLX_PQNT; + else + tmp &= CLXY_PAUSE_QUANTA_CLY_PQNT; + tmp |= ((u32)pause_time << (16 * (priority % 2))); + iowrite32be(tmp, ®s->pause_quanta[priority / 2]); + + tmp = ioread32be(®s->pause_thresh[priority / 2]); + if (priority % 2) + tmp &= CLXY_PAUSE_THRESH_CLX_QTH; + else + tmp &= CLXY_PAUSE_THRESH_CLY_QTH; + tmp |= ((u32)thresh_time << (16 * (priority % 2))); + iowrite32be(tmp, ®s->pause_thresh[priority / 2]); + + return 0; +} + +int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (en) + tmp &= ~CMD_CFG_PAUSE_IGNORE; + else + tmp |= CMD_CFG_PAUSE_IGNORE; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr) +{ + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + add_addr_in_paddr(memac->regs, (u8 *)(*enet_addr), 0); + + return 0; +} + +int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +{ + struct memac_regs __iomem *regs = memac->regs; + struct eth_hash_entry *hash_entry; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_ATOMIC); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &memac->multicast_addr_hash->lsts[hash]); + iowrite32be(hash | HASH_CTRL_MCAST_EN, ®s->hashtable_ctrl); + + return 0; +} + +int memac_set_allmulti(struct fman_mac *memac, bool enable) +{ + u32 entry; + struct memac_regs __iomem *regs = memac->regs; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + if (enable) { + for (entry = 0; entry < HASH_TABLE_SIZE; entry++) + iowrite32be(entry | HASH_CTRL_MCAST_EN, + ®s->hashtable_ctrl); + } else { + for (entry = 0; entry < HASH_TABLE_SIZE; entry++) + iowrite32be(entry & ~HASH_CTRL_MCAST_EN, + ®s->hashtable_ctrl); + } + + memac->allmulti_enabled = enable; + + return 0; +} + +int memac_set_tstamp(struct fman_mac *memac, bool enable) +{ + return 0; /* Always enabled. */ +} + +int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +{ + struct memac_regs __iomem *regs = memac->regs; + struct eth_hash_entry *hash_entry = NULL; + struct list_head *pos; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + list_for_each(pos, &memac->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry && hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + + if (!memac->allmulti_enabled) { + if (list_empty(&memac->multicast_addr_hash->lsts[hash])) + iowrite32be(hash & ~HASH_CTRL_MCAST_EN, + ®s->hashtable_ctrl); + } + + return 0; +} + +int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable) +{ + u32 bit_mask = 0; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + memac->exceptions |= bit_mask; + else + memac->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + set_exception(memac->regs, bit_mask, enable); + + return 0; +} + +int memac_init(struct fman_mac *memac) +{ + struct memac_cfg *memac_drv_param; + u8 i; + enet_addr_t eth_addr; + bool slow_10g_if = false; + struct fixed_phy_status *fixed_link; + int err; + u32 reg32 = 0; + + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + err = check_init_parameters(memac); + if (err) + return err; + + memac_drv_param = memac->memac_drv_param; + + if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4) + slow_10g_if = true; + + /* First, reset the MAC if desired. */ + if (memac_drv_param->reset_on_init) { + err = reset(memac->regs); + if (err) { + pr_err("mEMAC reset failed\n"); + return err; + } + } + + /* MAC Address */ + if (memac->addr != 0) { + MAKE_ENET_ADDR_FROM_UINT64(memac->addr, eth_addr); + add_addr_in_paddr(memac->regs, (u8 *)eth_addr, 0); + } + + fixed_link = memac_drv_param->fixed_link; + + init(memac->regs, memac->memac_drv_param, memac->phy_if, + memac->max_speed, slow_10g_if, memac->exceptions); + + /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround + * Exists only in FMan 6.0 and 6.3. + */ + if ((memac->fm_rev_info.major == 6) && + ((memac->fm_rev_info.minor == 0) || + (memac->fm_rev_info.minor == 3))) { + /* MAC strips CRC from received frames - this workaround + * should decrease the likelihood of bug appearance + */ + reg32 = ioread32be(&memac->regs->command_config); + reg32 &= ~CMD_CFG_CRC_FWD; + iowrite32be(reg32, &memac->regs->command_config); + } + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { + /* Configure internal SGMII PHY */ + if (memac->basex_if) + setup_sgmii_internal_phy_base_x(memac); + else + setup_sgmii_internal_phy(memac, fixed_link); + } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { + /* Configure 4 internal SGMII PHYs */ + for (i = 0; i < 4; i++) { + u8 qsmgii_phy_addr, phy_addr; + /* QSGMII PHY address occupies 3 upper bits of 5-bit + * phy_address; the lower 2 bits are used to extend + * register address space and access each one of 4 + * ports inside QSGMII. + */ + phy_addr = memac->pcsphy->mdio.addr; + qsmgii_phy_addr = (u8)((phy_addr << 2) | i); + memac->pcsphy->mdio.addr = qsmgii_phy_addr; + if (memac->basex_if) + setup_sgmii_internal_phy_base_x(memac); + else + setup_sgmii_internal_phy(memac, fixed_link); + + memac->pcsphy->mdio.addr = phy_addr; + } + } + + /* Max Frame Length */ + err = fman_set_mac_max_frame(memac->fm, memac->mac_id, + memac_drv_param->max_frame_length); + if (err) { + pr_err("settings Mac max frame length is FAILED\n"); + return err; + } + + memac->multicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->multicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + memac->unicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->unicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_ERR, memac_err_exception, memac); + + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL, memac_exception, memac); + + kfree(memac_drv_param); + memac->memac_drv_param = NULL; + + return 0; +} + +int memac_free(struct fman_mac *memac) +{ + free_init_resources(memac); + + if (memac->pcsphy) + put_device(&memac->pcsphy->mdio.dev); + + kfree(memac->memac_drv_param); + kfree(memac); + + return 0; +} + +struct fman_mac *memac_config(struct fman_mac_params *params) +{ + struct fman_mac *memac; + struct memac_cfg *memac_drv_param; + void __iomem *base_addr; + + base_addr = params->base_addr; + /* allocate memory for the m_emac data structure */ + memac = kzalloc(sizeof(*memac), GFP_KERNEL); + if (!memac) + return NULL; + + /* allocate memory for the m_emac driver parameters data structure */ + memac_drv_param = kzalloc(sizeof(*memac_drv_param), GFP_KERNEL); + if (!memac_drv_param) { + memac_free(memac); + return NULL; + } + + /* Plant parameter structure pointer */ + memac->memac_drv_param = memac_drv_param; + + set_dflts(memac_drv_param); + + memac->addr = ENET_ADDR_TO_UINT64(params->addr); + + memac->regs = base_addr; + memac->max_speed = params->max_speed; + memac->phy_if = params->phy_if; + memac->mac_id = params->mac_id; + memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | + MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI); + memac->exception_cb = params->exception_cb; + memac->event_cb = params->event_cb; + memac->dev_id = params->dev_id; + memac->fm = params->fm; + memac->basex_if = params->basex_if; + + /* Save FMan revision */ + fman_get_revision(memac->fm, &memac->fm_rev_info); + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || + memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { + if (!params->internal_phy_node) { + pr_err("PCS PHY node is not available\n"); + memac_free(memac); + return NULL; + } + + memac->pcsphy = of_phy_find_device(params->internal_phy_node); + if (!memac->pcsphy) { + pr_err("of_phy_find_device (PCS PHY) failed\n"); + memac_free(memac); + return NULL; + } + } + + return memac; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h new file mode 100644 index 000000000..b2c671ec0 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_memac.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __MEMAC_H +#define __MEMAC_H + +#include "fman_mac.h" + +#include <linux/netdevice.h> +#include <linux/phy_fixed.h> + +struct fman_mac *memac_config(struct fman_mac_params *params); +int memac_set_promiscuous(struct fman_mac *memac, bool new_val); +int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr); +int memac_adjust_link(struct fman_mac *memac, u16 speed); +int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val); +int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable); +int memac_cfg_fixed_link(struct fman_mac *memac, + struct fixed_phy_status *fixed_link); +int memac_enable(struct fman_mac *memac, enum comm_mode mode); +int memac_disable(struct fman_mac *memac, enum comm_mode mode); +int memac_init(struct fman_mac *memac); +int memac_free(struct fman_mac *memac); +int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en); +int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time); +int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable); +int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); +int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); +int memac_set_allmulti(struct fman_mac *memac, bool enable); +int memac_set_tstamp(struct fman_mac *memac, bool enable); + +#endif /* __MEMAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.c b/drivers/net/ethernet/freescale/fman/fman_muram.c new file mode 100644 index 000000000..7ad317e62 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_muram.c @@ -0,0 +1,159 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "fman_muram.h" + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/genalloc.h> + +struct muram_info { + struct gen_pool *pool; + void __iomem *vbase; + size_t size; + phys_addr_t pbase; +}; + +static unsigned long fman_muram_vbase_to_offset(struct muram_info *muram, + unsigned long vaddr) +{ + return vaddr - (unsigned long)muram->vbase; +} + +/** + * fman_muram_init + * @base: Pointer to base of memory mapped FM-MURAM. + * @size: Size of the FM-MURAM partition. + * + * Creates partition in the MURAM. + * The routine returns a pointer to the MURAM partition. + * This pointer must be passed as to all other FM-MURAM function calls. + * No actual initialization or configuration of FM_MURAM hardware is done by + * this routine. + * + * Return: pointer to FM-MURAM object, or NULL for Failure. + */ +struct muram_info *fman_muram_init(phys_addr_t base, size_t size) +{ + struct muram_info *muram; + void __iomem *vaddr; + int ret; + + muram = kzalloc(sizeof(*muram), GFP_KERNEL); + if (!muram) + return NULL; + + muram->pool = gen_pool_create(ilog2(64), -1); + if (!muram->pool) { + pr_err("%s(): MURAM pool create failed\n", __func__); + goto muram_free; + } + + vaddr = ioremap(base, size); + if (!vaddr) { + pr_err("%s(): MURAM ioremap failed\n", __func__); + goto pool_destroy; + } + + ret = gen_pool_add_virt(muram->pool, (unsigned long)vaddr, + base, size, -1); + if (ret < 0) { + pr_err("%s(): MURAM pool add failed\n", __func__); + iounmap(vaddr); + goto pool_destroy; + } + + memset_io(vaddr, 0, (int)size); + + muram->vbase = vaddr; + muram->pbase = base; + return muram; + +pool_destroy: + gen_pool_destroy(muram->pool); +muram_free: + kfree(muram); + return NULL; +} + +/** + * fman_muram_offset_to_vbase + * @muram: FM-MURAM module pointer. + * @offset: the offset of the memory block + * + * Gives the address of the memory region from specific offset + * + * Return: The address of the memory block + */ +unsigned long fman_muram_offset_to_vbase(struct muram_info *muram, + unsigned long offset) +{ + return offset + (unsigned long)muram->vbase; +} + +/** + * fman_muram_alloc + * @muram: FM-MURAM module pointer. + * @size: Size of the memory to be allocated. + * + * Allocate some memory from FM-MURAM partition. + * + * Return: address of the allocated memory; NULL otherwise. + */ +unsigned long fman_muram_alloc(struct muram_info *muram, size_t size) +{ + unsigned long vaddr; + + vaddr = gen_pool_alloc(muram->pool, size); + if (!vaddr) + return -ENOMEM; + + memset_io((void __iomem *)vaddr, 0, size); + + return fman_muram_vbase_to_offset(muram, vaddr); +} + +/** + * fman_muram_free_mem + * @muram: FM-MURAM module pointer. + * @offset: offset of the memory region to be freed. + * @size: size of the memory to be freed. + * + * Free an allocated memory from FM-MURAM partition. + */ +void fman_muram_free_mem(struct muram_info *muram, unsigned long offset, + size_t size) +{ + unsigned long addr = fman_muram_offset_to_vbase(muram, offset); + + gen_pool_free(muram->pool, addr, size); +} diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.h b/drivers/net/ethernet/freescale/fman/fman_muram.h new file mode 100644 index 000000000..453bf849e --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_muram.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FM_MURAM_EXT +#define __FM_MURAM_EXT + +#include <linux/types.h> + +#define FM_MURAM_INVALID_ALLOCATION -1 + +/* Structure for FM MURAM information */ +struct muram_info; + +struct muram_info *fman_muram_init(phys_addr_t base, size_t size); + +unsigned long fman_muram_offset_to_vbase(struct muram_info *muram, + unsigned long offset); + +unsigned long fman_muram_alloc(struct muram_info *muram, size_t size); + +void fman_muram_free_mem(struct muram_info *muram, unsigned long offset, + size_t size); + +#endif /* __FM_MURAM_EXT */ diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c new file mode 100644 index 000000000..4c9d05c45 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -0,0 +1,1947 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/delay.h> +#include <linux/libfdt_env.h> + +#include "fman.h" +#include "fman_port.h" +#include "fman_sp.h" +#include "fman_keygen.h" + +/* Queue ID */ +#define DFLT_FQ_ID 0x00FFFFFF + +/* General defines */ +#define PORT_BMI_FIFO_UNITS 0x100 + +#define MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) \ + min((u32)bmi_max_fifo_size, (u32)1024 * FMAN_BMI_FIFO_UNITS) + +#define PORT_CG_MAP_NUM 8 +#define PORT_PRS_RESULT_WORDS_NUM 8 +#define PORT_IC_OFFSET_UNITS 0x10 + +#define MIN_EXT_BUF_SIZE 64 + +#define BMI_PORT_REGS_OFFSET 0 +#define QMI_PORT_REGS_OFFSET 0x400 +#define HWP_PORT_REGS_OFFSET 0x800 + +/* Default values */ +#define DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN \ + DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN + +#define DFLT_PORT_CUT_BYTES_FROM_END 4 + +#define DFLT_PORT_ERRORS_TO_DISCARD FM_PORT_FRM_ERR_CLS_DISCARD +#define DFLT_PORT_MAX_FRAME_LENGTH 9600 + +#define DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(bmi_max_fifo_size) \ + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) + +#define DFLT_PORT_RX_FIFO_THRESHOLD(major, bmi_max_fifo_size) \ + (major == 6 ? \ + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) : \ + (MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) * 3 / 4)) \ + +#define DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS 0 + +/* QMI defines */ +#define QMI_DEQ_CFG_SUBPORTAL_MASK 0x1f + +#define QMI_PORT_CFG_EN 0x80000000 +#define QMI_PORT_STATUS_DEQ_FD_BSY 0x20000000 + +#define QMI_DEQ_CFG_PRI 0x80000000 +#define QMI_DEQ_CFG_TYPE1 0x10000000 +#define QMI_DEQ_CFG_TYPE2 0x20000000 +#define QMI_DEQ_CFG_TYPE3 0x30000000 +#define QMI_DEQ_CFG_PREFETCH_PARTIAL 0x01000000 +#define QMI_DEQ_CFG_PREFETCH_FULL 0x03000000 +#define QMI_DEQ_CFG_SP_MASK 0xf +#define QMI_DEQ_CFG_SP_SHIFT 20 + +#define QMI_BYTE_COUNT_LEVEL_CONTROL(_type) \ + (_type == FMAN_PORT_TYPE_TX ? 0x1400 : 0x400) + +/* BMI defins */ +#define BMI_EBD_EN 0x80000000 + +#define BMI_PORT_CFG_EN 0x80000000 + +#define BMI_PORT_STATUS_BSY 0x80000000 + +#define BMI_DMA_ATTR_SWP_SHIFT FMAN_SP_DMA_ATTR_SWP_SHIFT +#define BMI_DMA_ATTR_WRITE_OPTIMIZE FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE + +#define BMI_RX_FIFO_PRI_ELEVATION_SHIFT 16 +#define BMI_RX_FIFO_THRESHOLD_ETHE 0x80000000 + +#define BMI_FRAME_END_CS_IGNORE_SHIFT 24 +#define BMI_FRAME_END_CS_IGNORE_MASK 0x0000001f + +#define BMI_RX_FRAME_END_CUT_SHIFT 16 +#define BMI_RX_FRAME_END_CUT_MASK 0x0000001f + +#define BMI_IC_TO_EXT_SHIFT FMAN_SP_IC_TO_EXT_SHIFT +#define BMI_IC_TO_EXT_MASK 0x0000001f +#define BMI_IC_FROM_INT_SHIFT FMAN_SP_IC_FROM_INT_SHIFT +#define BMI_IC_FROM_INT_MASK 0x0000000f +#define BMI_IC_SIZE_MASK 0x0000001f + +#define BMI_INT_BUF_MARG_SHIFT 28 +#define BMI_INT_BUF_MARG_MASK 0x0000000f +#define BMI_EXT_BUF_MARG_START_SHIFT FMAN_SP_EXT_BUF_MARG_START_SHIFT +#define BMI_EXT_BUF_MARG_START_MASK 0x000001ff +#define BMI_EXT_BUF_MARG_END_MASK 0x000001ff + +#define BMI_CMD_MR_LEAC 0x00200000 +#define BMI_CMD_MR_SLEAC 0x00100000 +#define BMI_CMD_MR_MA 0x00080000 +#define BMI_CMD_MR_DEAS 0x00040000 +#define BMI_CMD_RX_MR_DEF (BMI_CMD_MR_LEAC | \ + BMI_CMD_MR_SLEAC | \ + BMI_CMD_MR_MA | \ + BMI_CMD_MR_DEAS) +#define BMI_CMD_TX_MR_DEF 0 + +#define BMI_CMD_ATTR_ORDER 0x80000000 +#define BMI_CMD_ATTR_SYNC 0x02000000 +#define BMI_CMD_ATTR_COLOR_SHIFT 26 + +#define BMI_FIFO_PIPELINE_DEPTH_SHIFT 12 +#define BMI_FIFO_PIPELINE_DEPTH_MASK 0x0000000f +#define BMI_NEXT_ENG_FD_BITS_SHIFT 24 + +#define BMI_EXT_BUF_POOL_VALID FMAN_SP_EXT_BUF_POOL_VALID +#define BMI_EXT_BUF_POOL_EN_COUNTER FMAN_SP_EXT_BUF_POOL_EN_COUNTER +#define BMI_EXT_BUF_POOL_BACKUP FMAN_SP_EXT_BUF_POOL_BACKUP +#define BMI_EXT_BUF_POOL_ID_SHIFT 16 +#define BMI_EXT_BUF_POOL_ID_MASK 0x003F0000 +#define BMI_POOL_DEP_NUM_OF_POOLS_SHIFT 16 + +#define BMI_TX_FIFO_MIN_FILL_SHIFT 16 + +#define BMI_PRIORITY_ELEVATION_LEVEL ((0x3FF + 1) * PORT_BMI_FIFO_UNITS) +#define BMI_FIFO_THRESHOLD ((0x3FF + 1) * PORT_BMI_FIFO_UNITS) + +#define BMI_DEQUEUE_PIPELINE_DEPTH(_type, _speed) \ + ((_type == FMAN_PORT_TYPE_TX && _speed == 10000) ? 4 : 1) + +#define RX_ERRS_TO_ENQ \ + (FM_PORT_FRM_ERR_DMA | \ + FM_PORT_FRM_ERR_PHYSICAL | \ + FM_PORT_FRM_ERR_SIZE | \ + FM_PORT_FRM_ERR_EXTRACTION | \ + FM_PORT_FRM_ERR_NO_SCHEME | \ + FM_PORT_FRM_ERR_PRS_TIMEOUT | \ + FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | \ + FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED | \ + FM_PORT_FRM_ERR_PRS_HDR_ERR | \ + FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW | \ + FM_PORT_FRM_ERR_IPRE) + +/* NIA defines */ +#define NIA_ORDER_RESTOR 0x00800000 +#define NIA_ENG_BMI 0x00500000 +#define NIA_ENG_QMI_ENQ 0x00540000 +#define NIA_ENG_QMI_DEQ 0x00580000 +#define NIA_ENG_HWP 0x00440000 +#define NIA_ENG_HWK 0x00480000 +#define NIA_BMI_AC_ENQ_FRAME 0x00000002 +#define NIA_BMI_AC_TX_RELEASE 0x000002C0 +#define NIA_BMI_AC_RELEASE 0x000000C0 +#define NIA_BMI_AC_TX 0x00000274 +#define NIA_BMI_AC_FETCH_ALL_FRAME 0x0000020c + +/* Port IDs */ +#define TX_10G_PORT_BASE 0x30 +#define RX_10G_PORT_BASE 0x10 + +/* BMI Rx port register map */ +struct fman_port_rx_bmi_regs { + u32 fmbm_rcfg; /* Rx Configuration */ + u32 fmbm_rst; /* Rx Status */ + u32 fmbm_rda; /* Rx DMA attributes */ + u32 fmbm_rfp; /* Rx FIFO Parameters */ + u32 fmbm_rfed; /* Rx Frame End Data */ + u32 fmbm_ricp; /* Rx Internal Context Parameters */ + u32 fmbm_rim; /* Rx Internal Buffer Margins */ + u32 fmbm_rebm; /* Rx External Buffer Margins */ + u32 fmbm_rfne; /* Rx Frame Next Engine */ + u32 fmbm_rfca; /* Rx Frame Command Attributes. */ + u32 fmbm_rfpne; /* Rx Frame Parser Next Engine */ + u32 fmbm_rpso; /* Rx Parse Start Offset */ + u32 fmbm_rpp; /* Rx Policer Profile */ + u32 fmbm_rccb; /* Rx Coarse Classification Base */ + u32 fmbm_reth; /* Rx Excessive Threshold */ + u32 reserved003c[1]; /* (0x03C 0x03F) */ + u32 fmbm_rprai[PORT_PRS_RESULT_WORDS_NUM]; + /* Rx Parse Results Array Init */ + u32 fmbm_rfqid; /* Rx Frame Queue ID */ + u32 fmbm_refqid; /* Rx Error Frame Queue ID */ + u32 fmbm_rfsdm; /* Rx Frame Status Discard Mask */ + u32 fmbm_rfsem; /* Rx Frame Status Error Mask */ + u32 fmbm_rfene; /* Rx Frame Enqueue Next Engine */ + u32 reserved0074[0x2]; /* (0x074-0x07C) */ + u32 fmbm_rcmne; /* Rx Frame Continuous Mode Next Engine */ + u32 reserved0080[0x20]; /* (0x080 0x0FF) */ + u32 fmbm_ebmpi[FMAN_PORT_MAX_EXT_POOLS_NUM]; + /* Buffer Manager pool Information- */ + u32 fmbm_acnt[FMAN_PORT_MAX_EXT_POOLS_NUM]; /* Allocate Counter- */ + u32 reserved0130[8]; /* 0x130/0x140 - 0x15F reserved - */ + u32 fmbm_rcgm[PORT_CG_MAP_NUM]; /* Congestion Group Map */ + u32 fmbm_mpd; /* BM Pool Depletion */ + u32 reserved0184[0x1F]; /* (0x184 0x1FF) */ + u32 fmbm_rstc; /* Rx Statistics Counters */ + u32 fmbm_rfrc; /* Rx Frame Counter */ + u32 fmbm_rfbc; /* Rx Bad Frames Counter */ + u32 fmbm_rlfc; /* Rx Large Frames Counter */ + u32 fmbm_rffc; /* Rx Filter Frames Counter */ + u32 fmbm_rfdc; /* Rx Frame Discard Counter */ + u32 fmbm_rfldec; /* Rx Frames List DMA Error Counter */ + u32 fmbm_rodc; /* Rx Out of Buffers Discard nntr */ + u32 fmbm_rbdc; /* Rx Buffers Deallocate Counter */ + u32 fmbm_rpec; /* RX Prepare to enqueue Counte */ + u32 reserved0224[0x16]; /* (0x224 0x27F) */ + u32 fmbm_rpc; /* Rx Performance Counters */ + u32 fmbm_rpcp; /* Rx Performance Count Parameters */ + u32 fmbm_rccn; /* Rx Cycle Counter */ + u32 fmbm_rtuc; /* Rx Tasks Utilization Counter */ + u32 fmbm_rrquc; /* Rx Receive Queue Utilization cntr */ + u32 fmbm_rduc; /* Rx DMA Utilization Counter */ + u32 fmbm_rfuc; /* Rx FIFO Utilization Counter */ + u32 fmbm_rpac; /* Rx Pause Activation Counter */ + u32 reserved02a0[0x18]; /* (0x2A0 0x2FF) */ + u32 fmbm_rdcfg[0x3]; /* Rx Debug Configuration */ + u32 fmbm_rgpr; /* Rx General Purpose Register */ + u32 reserved0310[0x3a]; +}; + +/* BMI Tx port register map */ +struct fman_port_tx_bmi_regs { + u32 fmbm_tcfg; /* Tx Configuration */ + u32 fmbm_tst; /* Tx Status */ + u32 fmbm_tda; /* Tx DMA attributes */ + u32 fmbm_tfp; /* Tx FIFO Parameters */ + u32 fmbm_tfed; /* Tx Frame End Data */ + u32 fmbm_ticp; /* Tx Internal Context Parameters */ + u32 fmbm_tfdne; /* Tx Frame Dequeue Next Engine. */ + u32 fmbm_tfca; /* Tx Frame Command attribute. */ + u32 fmbm_tcfqid; /* Tx Confirmation Frame Queue ID. */ + u32 fmbm_tefqid; /* Tx Frame Error Queue ID */ + u32 fmbm_tfene; /* Tx Frame Enqueue Next Engine */ + u32 fmbm_trlmts; /* Tx Rate Limiter Scale */ + u32 fmbm_trlmt; /* Tx Rate Limiter */ + u32 reserved0034[0x0e]; /* (0x034-0x6c) */ + u32 fmbm_tccb; /* Tx Coarse Classification base */ + u32 fmbm_tfne; /* Tx Frame Next Engine */ + u32 fmbm_tpfcm[0x02]; + /* Tx Priority based Flow Control (PFC) Mapping */ + u32 fmbm_tcmne; /* Tx Frame Continuous Mode Next Engine */ + u32 reserved0080[0x60]; /* (0x080-0x200) */ + u32 fmbm_tstc; /* Tx Statistics Counters */ + u32 fmbm_tfrc; /* Tx Frame Counter */ + u32 fmbm_tfdc; /* Tx Frames Discard Counter */ + u32 fmbm_tfledc; /* Tx Frame len error discard cntr */ + u32 fmbm_tfufdc; /* Tx Frame unsprt frmt discard cntr */ + u32 fmbm_tbdc; /* Tx Buffers Deallocate Counter */ + u32 reserved0218[0x1A]; /* (0x218-0x280) */ + u32 fmbm_tpc; /* Tx Performance Counters */ + u32 fmbm_tpcp; /* Tx Performance Count Parameters */ + u32 fmbm_tccn; /* Tx Cycle Counter */ + u32 fmbm_ttuc; /* Tx Tasks Utilization Counter */ + u32 fmbm_ttcquc; /* Tx Transmit conf Q util Counter */ + u32 fmbm_tduc; /* Tx DMA Utilization Counter */ + u32 fmbm_tfuc; /* Tx FIFO Utilization Counter */ + u32 reserved029c[16]; /* (0x29C-0x2FF) */ + u32 fmbm_tdcfg[0x3]; /* Tx Debug Configuration */ + u32 fmbm_tgpr; /* Tx General Purpose Register */ + u32 reserved0310[0x3a]; /* (0x310-0x3FF) */ +}; + +/* BMI port register map */ +union fman_port_bmi_regs { + struct fman_port_rx_bmi_regs rx; + struct fman_port_tx_bmi_regs tx; +}; + +/* QMI port register map */ +struct fman_port_qmi_regs { + u32 fmqm_pnc; /* PortID n Configuration Register */ + u32 fmqm_pns; /* PortID n Status Register */ + u32 fmqm_pnts; /* PortID n Task Status Register */ + u32 reserved00c[4]; /* 0xn00C - 0xn01B */ + u32 fmqm_pnen; /* PortID n Enqueue NIA Register */ + u32 fmqm_pnetfc; /* PortID n Enq Total Frame Counter */ + u32 reserved024[2]; /* 0xn024 - 0x02B */ + u32 fmqm_pndn; /* PortID n Dequeue NIA Register */ + u32 fmqm_pndc; /* PortID n Dequeue Config Register */ + u32 fmqm_pndtfc; /* PortID n Dequeue tot Frame cntr */ + u32 fmqm_pndfdc; /* PortID n Dequeue FQID Dflt Cntr */ + u32 fmqm_pndcc; /* PortID n Dequeue Confirm Counter */ +}; + +#define HWP_HXS_COUNT 16 +#define HWP_HXS_PHE_REPORT 0x00000800 +#define HWP_HXS_PCAC_PSTAT 0x00000100 +#define HWP_HXS_PCAC_PSTOP 0x00000001 +#define HWP_HXS_TCP_OFFSET 0xA +#define HWP_HXS_UDP_OFFSET 0xB +#define HWP_HXS_SH_PAD_REM 0x80000000 + +struct fman_port_hwp_regs { + struct { + u32 ssa; /* Soft Sequence Attachment */ + u32 lcv; /* Line-up Enable Confirmation Mask */ + } pmda[HWP_HXS_COUNT]; /* Parse Memory Direct Access Registers */ + u32 reserved080[(0x3f8 - 0x080) / 4]; /* (0x080-0x3f7) */ + u32 fmpr_pcac; /* Configuration Access Control */ +}; + +/* QMI dequeue prefetch modes */ +enum fman_port_deq_prefetch { + FMAN_PORT_DEQ_NO_PREFETCH, /* No prefetch mode */ + FMAN_PORT_DEQ_PART_PREFETCH, /* Partial prefetch mode */ + FMAN_PORT_DEQ_FULL_PREFETCH /* Full prefetch mode */ +}; + +/* A structure for defining FM port resources */ +struct fman_port_rsrc { + u32 num; /* Committed required resource */ + u32 extra; /* Extra (not committed) required resource */ +}; + +enum fman_port_dma_swap { + FMAN_PORT_DMA_NO_SWAP, /* No swap, transfer data as is */ + FMAN_PORT_DMA_SWAP_LE, + /* The transferred data should be swapped in PPC Little Endian mode */ + FMAN_PORT_DMA_SWAP_BE + /* The transferred data should be swapped in Big Endian mode */ +}; + +/* Default port color */ +enum fman_port_color { + FMAN_PORT_COLOR_GREEN, /* Default port color is green */ + FMAN_PORT_COLOR_YELLOW, /* Default port color is yellow */ + FMAN_PORT_COLOR_RED, /* Default port color is red */ + FMAN_PORT_COLOR_OVERRIDE /* Ignore color */ +}; + +/* QMI dequeue from the SP channel - types */ +enum fman_port_deq_type { + FMAN_PORT_DEQ_BY_PRI, + /* Priority precedence and Intra-Class scheduling */ + FMAN_PORT_DEQ_ACTIVE_FQ, + /* Active FQ precedence and Intra-Class scheduling */ + FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS + /* Active FQ precedence and override Intra-Class scheduling */ +}; + +/* External buffer pools configuration */ +struct fman_port_bpools { + u8 count; /* Num of pools to set up */ + bool counters_enable; /* Enable allocate counters */ + u8 grp_bp_depleted_num; + /* Number of depleted pools - if reached the BMI indicates + * the MAC to send a pause frame + */ + struct { + u8 bpid; /* BM pool ID */ + u16 size; + /* Pool's size - must be in ascending order */ + bool is_backup; + /* If this is a backup pool */ + bool grp_bp_depleted; + /* Consider this buffer in multiple pools depletion criteria */ + bool single_bp_depleted; + /* Consider this buffer in single pool depletion criteria */ + } bpool[FMAN_PORT_MAX_EXT_POOLS_NUM]; +}; + +struct fman_port_cfg { + u32 dflt_fqid; + u32 err_fqid; + u32 pcd_base_fqid; + u32 pcd_fqs_count; + u8 deq_sp; + bool deq_high_priority; + enum fman_port_deq_type deq_type; + enum fman_port_deq_prefetch deq_prefetch_option; + u16 deq_byte_cnt; + u8 cheksum_last_bytes_ignore; + u8 rx_cut_end_bytes; + struct fman_buf_pool_depletion buf_pool_depletion; + struct fman_ext_pools ext_buf_pools; + u32 tx_fifo_min_level; + u32 tx_fifo_low_comf_level; + u32 rx_pri_elevation; + u32 rx_fifo_thr; + struct fman_sp_buf_margins buf_margins; + u32 int_buf_start_margin; + struct fman_sp_int_context_data_copy int_context; + u32 discard_mask; + u32 err_mask; + struct fman_buffer_prefix_content buffer_prefix_content; + bool dont_release_buf; + + u8 rx_fd_bits; + u32 tx_fifo_deq_pipeline_depth; + bool errata_A006320; + bool excessive_threshold_register; + bool fmbm_tfne_has_features; + + enum fman_port_dma_swap dma_swap_data; + enum fman_port_color color; +}; + +struct fman_port_rx_pools_params { + u8 num_of_pools; + u16 largest_buf_size; +}; + +struct fman_port_dts_params { + void __iomem *base_addr; /* FMan port virtual memory */ + enum fman_port_type type; /* Port type */ + u16 speed; /* Port speed */ + u8 id; /* HW Port Id */ + u32 qman_channel_id; /* QMan channel id (non RX only) */ + struct fman *fman; /* FMan Handle */ +}; + +struct fman_port { + void *fm; + struct device *dev; + struct fman_rev_info rev_info; + u8 port_id; + enum fman_port_type port_type; + u16 port_speed; + + union fman_port_bmi_regs __iomem *bmi_regs; + struct fman_port_qmi_regs __iomem *qmi_regs; + struct fman_port_hwp_regs __iomem *hwp_regs; + + struct fman_sp_buffer_offsets buffer_offsets; + + u8 internal_buf_offset; + struct fman_ext_pools ext_buf_pools; + + u16 max_frame_length; + struct fman_port_rsrc open_dmas; + struct fman_port_rsrc tasks; + struct fman_port_rsrc fifo_bufs; + struct fman_port_rx_pools_params rx_pools_params; + + struct fman_port_cfg *cfg; + struct fman_port_dts_params dts_params; + + u8 ext_pools_num; + u32 max_port_fifo_size; + u32 max_num_of_ext_pools; + u32 max_num_of_sub_portals; + u32 bm_max_num_of_pools; +}; + +static int init_bmi_rx(struct fman_port *port) +{ + struct fman_port_rx_bmi_regs __iomem *regs = &port->bmi_regs->rx; + struct fman_port_cfg *cfg = port->cfg; + u32 tmp; + + /* DMA attributes */ + tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT; + /* Enable write optimization */ + tmp |= BMI_DMA_ATTR_WRITE_OPTIMIZE; + iowrite32be(tmp, ®s->fmbm_rda); + + /* Rx FIFO parameters */ + tmp = (cfg->rx_pri_elevation / PORT_BMI_FIFO_UNITS - 1) << + BMI_RX_FIFO_PRI_ELEVATION_SHIFT; + tmp |= cfg->rx_fifo_thr / PORT_BMI_FIFO_UNITS - 1; + iowrite32be(tmp, ®s->fmbm_rfp); + + if (cfg->excessive_threshold_register) + /* always allow access to the extra resources */ + iowrite32be(BMI_RX_FIFO_THRESHOLD_ETHE, ®s->fmbm_reth); + + /* Frame end data */ + tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) << + BMI_FRAME_END_CS_IGNORE_SHIFT; + tmp |= (cfg->rx_cut_end_bytes & BMI_RX_FRAME_END_CUT_MASK) << + BMI_RX_FRAME_END_CUT_SHIFT; + if (cfg->errata_A006320) + tmp &= 0xffe0ffff; + iowrite32be(tmp, ®s->fmbm_rfed); + + /* Internal context parameters */ + tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT; + tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT; + tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) & + BMI_IC_SIZE_MASK; + iowrite32be(tmp, ®s->fmbm_ricp); + + /* Internal buffer offset */ + tmp = ((cfg->int_buf_start_margin / PORT_IC_OFFSET_UNITS) & + BMI_INT_BUF_MARG_MASK) << BMI_INT_BUF_MARG_SHIFT; + iowrite32be(tmp, ®s->fmbm_rim); + + /* External buffer margins */ + tmp = (cfg->buf_margins.start_margins & BMI_EXT_BUF_MARG_START_MASK) << + BMI_EXT_BUF_MARG_START_SHIFT; + tmp |= cfg->buf_margins.end_margins & BMI_EXT_BUF_MARG_END_MASK; + iowrite32be(tmp, ®s->fmbm_rebm); + + /* Frame attributes */ + tmp = BMI_CMD_RX_MR_DEF; + tmp |= BMI_CMD_ATTR_ORDER; + tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT; + /* Synchronization request */ + tmp |= BMI_CMD_ATTR_SYNC; + + iowrite32be(tmp, ®s->fmbm_rfca); + + /* NIA */ + tmp = (u32)cfg->rx_fd_bits << BMI_NEXT_ENG_FD_BITS_SHIFT; + + tmp |= NIA_ENG_HWP; + iowrite32be(tmp, ®s->fmbm_rfne); + + /* Parser Next Engine NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME, ®s->fmbm_rfpne); + + /* Enqueue NIA */ + iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, ®s->fmbm_rfene); + + /* Default/error queues */ + iowrite32be((cfg->dflt_fqid & DFLT_FQ_ID), ®s->fmbm_rfqid); + iowrite32be((cfg->err_fqid & DFLT_FQ_ID), ®s->fmbm_refqid); + + /* Discard/error masks */ + iowrite32be(cfg->discard_mask, ®s->fmbm_rfsdm); + iowrite32be(cfg->err_mask, ®s->fmbm_rfsem); + + return 0; +} + +static int init_bmi_tx(struct fman_port *port) +{ + struct fman_port_tx_bmi_regs __iomem *regs = &port->bmi_regs->tx; + struct fman_port_cfg *cfg = port->cfg; + u32 tmp; + + /* Tx Configuration register */ + tmp = 0; + iowrite32be(tmp, ®s->fmbm_tcfg); + + /* DMA attributes */ + tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT; + iowrite32be(tmp, ®s->fmbm_tda); + + /* Tx FIFO parameters */ + tmp = (cfg->tx_fifo_min_level / PORT_BMI_FIFO_UNITS) << + BMI_TX_FIFO_MIN_FILL_SHIFT; + tmp |= ((cfg->tx_fifo_deq_pipeline_depth - 1) & + BMI_FIFO_PIPELINE_DEPTH_MASK) << BMI_FIFO_PIPELINE_DEPTH_SHIFT; + tmp |= (cfg->tx_fifo_low_comf_level / PORT_BMI_FIFO_UNITS) - 1; + iowrite32be(tmp, ®s->fmbm_tfp); + + /* Frame end data */ + tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) << + BMI_FRAME_END_CS_IGNORE_SHIFT; + iowrite32be(tmp, ®s->fmbm_tfed); + + /* Internal context parameters */ + tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT; + tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT; + tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) & + BMI_IC_SIZE_MASK; + iowrite32be(tmp, ®s->fmbm_ticp); + + /* Frame attributes */ + tmp = BMI_CMD_TX_MR_DEF; + tmp |= BMI_CMD_ATTR_ORDER; + tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT; + iowrite32be(tmp, ®s->fmbm_tfca); + + /* Dequeue NIA + enqueue NIA */ + iowrite32be(NIA_ENG_QMI_DEQ, ®s->fmbm_tfdne); + iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, ®s->fmbm_tfene); + if (cfg->fmbm_tfne_has_features) + iowrite32be(!cfg->dflt_fqid ? + BMI_EBD_EN | NIA_BMI_AC_FETCH_ALL_FRAME : + NIA_BMI_AC_FETCH_ALL_FRAME, ®s->fmbm_tfne); + if (!cfg->dflt_fqid && cfg->dont_release_buf) { + iowrite32be(DFLT_FQ_ID, ®s->fmbm_tcfqid); + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE, + ®s->fmbm_tfene); + if (cfg->fmbm_tfne_has_features) + iowrite32be(ioread32be(®s->fmbm_tfne) & ~BMI_EBD_EN, + ®s->fmbm_tfne); + } + + /* Confirmation/error queues */ + if (cfg->dflt_fqid || !cfg->dont_release_buf) + iowrite32be(cfg->dflt_fqid & DFLT_FQ_ID, ®s->fmbm_tcfqid); + iowrite32be((cfg->err_fqid & DFLT_FQ_ID), ®s->fmbm_tefqid); + + return 0; +} + +static int init_qmi(struct fman_port *port) +{ + struct fman_port_qmi_regs __iomem *regs = port->qmi_regs; + struct fman_port_cfg *cfg = port->cfg; + u32 tmp; + + /* Rx port configuration */ + if (port->port_type == FMAN_PORT_TYPE_RX) { + /* Enqueue NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_RELEASE, ®s->fmqm_pnen); + return 0; + } + + /* Continue with Tx port configuration */ + if (port->port_type == FMAN_PORT_TYPE_TX) { + /* Enqueue NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE, + ®s->fmqm_pnen); + /* Dequeue NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX, ®s->fmqm_pndn); + } + + /* Dequeue Configuration register */ + tmp = 0; + if (cfg->deq_high_priority) + tmp |= QMI_DEQ_CFG_PRI; + + switch (cfg->deq_type) { + case FMAN_PORT_DEQ_BY_PRI: + tmp |= QMI_DEQ_CFG_TYPE1; + break; + case FMAN_PORT_DEQ_ACTIVE_FQ: + tmp |= QMI_DEQ_CFG_TYPE2; + break; + case FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS: + tmp |= QMI_DEQ_CFG_TYPE3; + break; + default: + return -EINVAL; + } + + switch (cfg->deq_prefetch_option) { + case FMAN_PORT_DEQ_NO_PREFETCH: + break; + case FMAN_PORT_DEQ_PART_PREFETCH: + tmp |= QMI_DEQ_CFG_PREFETCH_PARTIAL; + break; + case FMAN_PORT_DEQ_FULL_PREFETCH: + tmp |= QMI_DEQ_CFG_PREFETCH_FULL; + break; + default: + return -EINVAL; + } + + tmp |= (cfg->deq_sp & QMI_DEQ_CFG_SP_MASK) << QMI_DEQ_CFG_SP_SHIFT; + tmp |= cfg->deq_byte_cnt; + iowrite32be(tmp, ®s->fmqm_pndc); + + return 0; +} + +static void stop_port_hwp(struct fman_port *port) +{ + struct fman_port_hwp_regs __iomem *regs = port->hwp_regs; + int cnt = 100; + + iowrite32be(HWP_HXS_PCAC_PSTOP, ®s->fmpr_pcac); + + while (cnt-- > 0 && + (ioread32be(®s->fmpr_pcac) & HWP_HXS_PCAC_PSTAT)) + udelay(10); + if (!cnt) + pr_err("Timeout stopping HW Parser\n"); +} + +static void start_port_hwp(struct fman_port *port) +{ + struct fman_port_hwp_regs __iomem *regs = port->hwp_regs; + int cnt = 100; + + iowrite32be(0, ®s->fmpr_pcac); + + while (cnt-- > 0 && + !(ioread32be(®s->fmpr_pcac) & HWP_HXS_PCAC_PSTAT)) + udelay(10); + if (!cnt) + pr_err("Timeout starting HW Parser\n"); +} + +static void init_hwp(struct fman_port *port) +{ + struct fman_port_hwp_regs __iomem *regs = port->hwp_regs; + int i; + + stop_port_hwp(port); + + for (i = 0; i < HWP_HXS_COUNT; i++) { + /* enable HXS error reporting into FD[STATUS] PHE */ + iowrite32be(0x00000000, ®s->pmda[i].ssa); + iowrite32be(0xffffffff, ®s->pmda[i].lcv); + } + + /* Short packet padding removal from checksum calculation */ + iowrite32be(HWP_HXS_SH_PAD_REM, ®s->pmda[HWP_HXS_TCP_OFFSET].ssa); + iowrite32be(HWP_HXS_SH_PAD_REM, ®s->pmda[HWP_HXS_UDP_OFFSET].ssa); + + start_port_hwp(port); +} + +static int init(struct fman_port *port) +{ + int err; + + /* Init BMI registers */ + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + err = init_bmi_rx(port); + if (!err) + init_hwp(port); + break; + case FMAN_PORT_TYPE_TX: + err = init_bmi_tx(port); + break; + default: + return -EINVAL; + } + + if (err) + return err; + + /* Init QMI registers */ + err = init_qmi(port); + if (err) + return err; + + return 0; +} + +static int set_bpools(const struct fman_port *port, + const struct fman_port_bpools *bp) +{ + u32 __iomem *bp_reg, *bp_depl_reg; + u32 tmp; + u8 i, max_bp_num; + bool grp_depl_used = false, rx_port; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + max_bp_num = port->ext_pools_num; + rx_port = true; + bp_reg = port->bmi_regs->rx.fmbm_ebmpi; + bp_depl_reg = &port->bmi_regs->rx.fmbm_mpd; + break; + default: + return -EINVAL; + } + + if (rx_port) { + /* Check buffers are provided in ascending order */ + for (i = 0; (i < (bp->count - 1) && + (i < FMAN_PORT_MAX_EXT_POOLS_NUM - 1)); i++) { + if (bp->bpool[i].size > bp->bpool[i + 1].size) + return -EINVAL; + } + } + + /* Set up external buffers pools */ + for (i = 0; i < bp->count; i++) { + tmp = BMI_EXT_BUF_POOL_VALID; + tmp |= ((u32)bp->bpool[i].bpid << + BMI_EXT_BUF_POOL_ID_SHIFT) & BMI_EXT_BUF_POOL_ID_MASK; + + if (rx_port) { + if (bp->counters_enable) + tmp |= BMI_EXT_BUF_POOL_EN_COUNTER; + + if (bp->bpool[i].is_backup) + tmp |= BMI_EXT_BUF_POOL_BACKUP; + + tmp |= (u32)bp->bpool[i].size; + } + + iowrite32be(tmp, &bp_reg[i]); + } + + /* Clear unused pools */ + for (i = bp->count; i < max_bp_num; i++) + iowrite32be(0, &bp_reg[i]); + + /* Pools depletion */ + tmp = 0; + for (i = 0; i < FMAN_PORT_MAX_EXT_POOLS_NUM; i++) { + if (bp->bpool[i].grp_bp_depleted) { + grp_depl_used = true; + tmp |= 0x80000000 >> i; + } + + if (bp->bpool[i].single_bp_depleted) + tmp |= 0x80 >> i; + } + + if (grp_depl_used) + tmp |= ((u32)bp->grp_bp_depleted_num - 1) << + BMI_POOL_DEP_NUM_OF_POOLS_SHIFT; + + iowrite32be(tmp, bp_depl_reg); + return 0; +} + +static bool is_init_done(struct fman_port_cfg *cfg) +{ + /* Checks if FMan port driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +static int verify_size_of_fifo(struct fman_port *port) +{ + u32 min_fifo_size_required = 0, opt_fifo_size_for_b2b = 0; + + /* TX Ports */ + if (port->port_type == FMAN_PORT_TYPE_TX) { + min_fifo_size_required = (u32) + (roundup(port->max_frame_length, + FMAN_BMI_FIFO_UNITS) + (3 * FMAN_BMI_FIFO_UNITS)); + + min_fifo_size_required += + port->cfg->tx_fifo_deq_pipeline_depth * + FMAN_BMI_FIFO_UNITS; + + opt_fifo_size_for_b2b = min_fifo_size_required; + + /* Add some margin for back-to-back capability to improve + * performance, allows the hardware to pipeline new frame dma + * while the previous frame not yet transmitted. + */ + if (port->port_speed == 10000) + opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS; + else + opt_fifo_size_for_b2b += 2 * FMAN_BMI_FIFO_UNITS; + } + + /* RX Ports */ + else if (port->port_type == FMAN_PORT_TYPE_RX) { + if (port->rev_info.major >= 6) + min_fifo_size_required = (u32) + (roundup(port->max_frame_length, + FMAN_BMI_FIFO_UNITS) + + (5 * FMAN_BMI_FIFO_UNITS)); + /* 4 according to spec + 1 for FOF>0 */ + else + min_fifo_size_required = (u32) + (roundup(min(port->max_frame_length, + port->rx_pools_params.largest_buf_size), + FMAN_BMI_FIFO_UNITS) + + (7 * FMAN_BMI_FIFO_UNITS)); + + opt_fifo_size_for_b2b = min_fifo_size_required; + + /* Add some margin for back-to-back capability to improve + * performance,allows the hardware to pipeline new frame dma + * while the previous frame not yet transmitted. + */ + if (port->port_speed == 10000) + opt_fifo_size_for_b2b += 8 * FMAN_BMI_FIFO_UNITS; + else + opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS; + } + + WARN_ON(min_fifo_size_required <= 0); + WARN_ON(opt_fifo_size_for_b2b < min_fifo_size_required); + + /* Verify the size */ + if (port->fifo_bufs.num < min_fifo_size_required) + dev_dbg(port->dev, "%s: FIFO size should be enlarged to %d bytes\n", + __func__, min_fifo_size_required); + else if (port->fifo_bufs.num < opt_fifo_size_for_b2b) + dev_dbg(port->dev, "%s: For b2b processing,FIFO may be enlarged to %d bytes\n", + __func__, opt_fifo_size_for_b2b); + + return 0; +} + +static int set_ext_buffer_pools(struct fman_port *port) +{ + struct fman_ext_pools *ext_buf_pools = &port->cfg->ext_buf_pools; + struct fman_buf_pool_depletion *buf_pool_depletion = + &port->cfg->buf_pool_depletion; + u8 ordered_array[FMAN_PORT_MAX_EXT_POOLS_NUM]; + u16 sizes_array[BM_MAX_NUM_OF_POOLS]; + int i = 0, j = 0, err; + struct fman_port_bpools bpools; + + memset(&ordered_array, 0, sizeof(u8) * FMAN_PORT_MAX_EXT_POOLS_NUM); + memset(&sizes_array, 0, sizeof(u16) * BM_MAX_NUM_OF_POOLS); + memcpy(&port->ext_buf_pools, ext_buf_pools, + sizeof(struct fman_ext_pools)); + + fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(ext_buf_pools, + ordered_array, + sizes_array); + + memset(&bpools, 0, sizeof(struct fman_port_bpools)); + bpools.count = ext_buf_pools->num_of_pools_used; + bpools.counters_enable = true; + for (i = 0; i < ext_buf_pools->num_of_pools_used; i++) { + bpools.bpool[i].bpid = ordered_array[i]; + bpools.bpool[i].size = sizes_array[ordered_array[i]]; + } + + /* save pools parameters for later use */ + port->rx_pools_params.num_of_pools = ext_buf_pools->num_of_pools_used; + port->rx_pools_params.largest_buf_size = + sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 1]]; + + /* FMBM_RMPD reg. - pool depletion */ + if (buf_pool_depletion->pools_grp_mode_enable) { + bpools.grp_bp_depleted_num = buf_pool_depletion->num_of_pools; + for (i = 0; i < port->bm_max_num_of_pools; i++) { + if (buf_pool_depletion->pools_to_consider[i]) { + for (j = 0; j < ext_buf_pools-> + num_of_pools_used; j++) { + if (i == ordered_array[j]) { + bpools.bpool[j]. + grp_bp_depleted = true; + break; + } + } + } + } + } + + if (buf_pool_depletion->single_pool_mode_enable) { + for (i = 0; i < port->bm_max_num_of_pools; i++) { + if (buf_pool_depletion-> + pools_to_consider_for_single_mode[i]) { + for (j = 0; j < ext_buf_pools-> + num_of_pools_used; j++) { + if (i == ordered_array[j]) { + bpools.bpool[j]. + single_bp_depleted = true; + break; + } + } + } + } + } + + err = set_bpools(port, &bpools); + if (err != 0) { + dev_err(port->dev, "%s: set_bpools() failed\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int init_low_level_driver(struct fman_port *port) +{ + struct fman_port_cfg *cfg = port->cfg; + u32 tmp_val; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + cfg->err_mask = (RX_ERRS_TO_ENQ & ~cfg->discard_mask); + break; + default: + break; + } + + tmp_val = (u32)((port->internal_buf_offset % OFFSET_UNITS) ? + (port->internal_buf_offset / OFFSET_UNITS + 1) : + (port->internal_buf_offset / OFFSET_UNITS)); + port->internal_buf_offset = (u8)(tmp_val * OFFSET_UNITS); + port->cfg->int_buf_start_margin = port->internal_buf_offset; + + if (init(port) != 0) { + dev_err(port->dev, "%s: fman port initialization failed\n", + __func__); + return -ENODEV; + } + + /* The code bellow is a trick so the FM will not release the buffer + * to BM nor will try to enqueue the frame to QM + */ + if (port->port_type == FMAN_PORT_TYPE_TX) { + if (!cfg->dflt_fqid && cfg->dont_release_buf) { + /* override fmbm_tcfqid 0 with a false non-0 value. + * This will force FM to act according to tfene. + * Otherwise, if fmbm_tcfqid is 0 the FM will release + * buffers to BM regardless of fmbm_tfene + */ + iowrite32be(0xFFFFFF, &port->bmi_regs->tx.fmbm_tcfqid); + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE, + &port->bmi_regs->tx.fmbm_tfene); + } + } + + return 0; +} + +static int fill_soc_specific_params(struct fman_port *port) +{ + u32 bmi_max_fifo_size; + + bmi_max_fifo_size = fman_get_bmi_max_fifo_size(port->fm); + port->max_port_fifo_size = MAX_PORT_FIFO_SIZE(bmi_max_fifo_size); + port->bm_max_num_of_pools = 64; + + /* P4080 - Major 2 + * P2041/P3041/P5020/P5040 - Major 3 + * Tx/Bx - Major 6 + */ + switch (port->rev_info.major) { + case 2: + case 3: + port->max_num_of_ext_pools = 4; + port->max_num_of_sub_portals = 12; + break; + + case 6: + port->max_num_of_ext_pools = 8; + port->max_num_of_sub_portals = 16; + break; + + default: + dev_err(port->dev, "%s: Unsupported FMan version\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int get_dflt_fifo_deq_pipeline_depth(u8 major, enum fman_port_type type, + u16 speed) +{ + switch (type) { + case FMAN_PORT_TYPE_RX: + case FMAN_PORT_TYPE_TX: + switch (speed) { + case 10000: + return 4; + case 1000: + if (major >= 6) + return 2; + else + return 1; + default: + return 0; + } + default: + return 0; + } +} + +static int get_dflt_num_of_tasks(u8 major, enum fman_port_type type, + u16 speed) +{ + switch (type) { + case FMAN_PORT_TYPE_RX: + case FMAN_PORT_TYPE_TX: + switch (speed) { + case 10000: + return 16; + case 1000: + if (major >= 6) + return 4; + else + return 3; + default: + return 0; + } + default: + return 0; + } +} + +static int get_dflt_extra_num_of_tasks(u8 major, enum fman_port_type type, + u16 speed) +{ + switch (type) { + case FMAN_PORT_TYPE_RX: + /* FMan V3 */ + if (major >= 6) + return 0; + + /* FMan V2 */ + if (speed == 10000) + return 8; + else + return 2; + case FMAN_PORT_TYPE_TX: + default: + return 0; + } +} + +static int get_dflt_num_of_open_dmas(u8 major, enum fman_port_type type, + u16 speed) +{ + int val; + + if (major >= 6) { + switch (type) { + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + val = 12; + else + val = 3; + break; + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 8; + else + val = 2; + break; + default: + return 0; + } + } else { + switch (type) { + case FMAN_PORT_TYPE_TX: + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 8; + else + val = 1; + break; + default: + val = 0; + } + } + + return val; +} + +static int get_dflt_extra_num_of_open_dmas(u8 major, enum fman_port_type type, + u16 speed) +{ + /* FMan V3 */ + if (major >= 6) + return 0; + + /* FMan V2 */ + switch (type) { + case FMAN_PORT_TYPE_RX: + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + return 8; + else + return 1; + default: + return 0; + } +} + +static int get_dflt_num_of_fifo_bufs(u8 major, enum fman_port_type type, + u16 speed) +{ + int val; + + if (major >= 6) { + switch (type) { + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + val = 64; + else + val = 50; + break; + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 96; + else + val = 50; + break; + default: + val = 0; + } + } else { + switch (type) { + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + val = 48; + else + val = 44; + break; + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 48; + else + val = 45; + break; + default: + val = 0; + } + } + + return val; +} + +static void set_dflt_cfg(struct fman_port *port, + struct fman_port_params *port_params) +{ + struct fman_port_cfg *cfg = port->cfg; + + cfg->dma_swap_data = FMAN_PORT_DMA_NO_SWAP; + cfg->color = FMAN_PORT_COLOR_GREEN; + cfg->rx_cut_end_bytes = DFLT_PORT_CUT_BYTES_FROM_END; + cfg->rx_pri_elevation = BMI_PRIORITY_ELEVATION_LEVEL; + cfg->rx_fifo_thr = BMI_FIFO_THRESHOLD; + cfg->tx_fifo_low_comf_level = (5 * 1024); + cfg->deq_type = FMAN_PORT_DEQ_BY_PRI; + cfg->deq_prefetch_option = FMAN_PORT_DEQ_FULL_PREFETCH; + cfg->tx_fifo_deq_pipeline_depth = + BMI_DEQUEUE_PIPELINE_DEPTH(port->port_type, port->port_speed); + cfg->deq_byte_cnt = QMI_BYTE_COUNT_LEVEL_CONTROL(port->port_type); + + cfg->rx_pri_elevation = + DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(port->max_port_fifo_size); + port->cfg->rx_fifo_thr = + DFLT_PORT_RX_FIFO_THRESHOLD(port->rev_info.major, + port->max_port_fifo_size); + + if ((port->rev_info.major == 6) && + ((port->rev_info.minor == 0) || (port->rev_info.minor == 3))) + cfg->errata_A006320 = true; + + /* Excessive Threshold register - exists for pre-FMv3 chips only */ + if (port->rev_info.major < 6) + cfg->excessive_threshold_register = true; + else + cfg->fmbm_tfne_has_features = true; + + cfg->buffer_prefix_content.data_align = + DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN; +} + +static void set_rx_dflt_cfg(struct fman_port *port, + struct fman_port_params *port_params) +{ + port->cfg->discard_mask = DFLT_PORT_ERRORS_TO_DISCARD; + + memcpy(&port->cfg->ext_buf_pools, + &port_params->specific_params.rx_params.ext_buf_pools, + sizeof(struct fman_ext_pools)); + port->cfg->err_fqid = + port_params->specific_params.rx_params.err_fqid; + port->cfg->dflt_fqid = + port_params->specific_params.rx_params.dflt_fqid; + port->cfg->pcd_base_fqid = + port_params->specific_params.rx_params.pcd_base_fqid; + port->cfg->pcd_fqs_count = + port_params->specific_params.rx_params.pcd_fqs_count; +} + +static void set_tx_dflt_cfg(struct fman_port *port, + struct fman_port_params *port_params, + struct fman_port_dts_params *dts_params) +{ + port->cfg->tx_fifo_deq_pipeline_depth = + get_dflt_fifo_deq_pipeline_depth(port->rev_info.major, + port->port_type, + port->port_speed); + port->cfg->err_fqid = + port_params->specific_params.non_rx_params.err_fqid; + port->cfg->deq_sp = + (u8)(dts_params->qman_channel_id & QMI_DEQ_CFG_SUBPORTAL_MASK); + port->cfg->dflt_fqid = + port_params->specific_params.non_rx_params.dflt_fqid; + port->cfg->deq_high_priority = true; +} + +/** + * fman_port_config + * @port: Pointer to the port structure + * @params: Pointer to data structure of parameters + * + * Creates a descriptor for the FM PORT module. + * The routine returns a pointer to the FM PORT object. + * This descriptor must be passed as first parameter to all other FM PORT + * function calls. + * No actual initialization or configuration of FM hardware is done by this + * routine. + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_config(struct fman_port *port, struct fman_port_params *params) +{ + void __iomem *base_addr = port->dts_params.base_addr; + int err; + + /* Allocate the FM driver's parameters structure */ + port->cfg = kzalloc(sizeof(*port->cfg), GFP_KERNEL); + if (!port->cfg) + return -EINVAL; + + /* Initialize FM port parameters which will be kept by the driver */ + port->port_type = port->dts_params.type; + port->port_speed = port->dts_params.speed; + port->port_id = port->dts_params.id; + port->fm = port->dts_params.fman; + port->ext_pools_num = (u8)8; + + /* get FM revision */ + fman_get_revision(port->fm, &port->rev_info); + + err = fill_soc_specific_params(port); + if (err) + goto err_port_cfg; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + set_rx_dflt_cfg(port, params); + fallthrough; + case FMAN_PORT_TYPE_TX: + set_tx_dflt_cfg(port, params, &port->dts_params); + fallthrough; + default: + set_dflt_cfg(port, params); + } + + /* Continue with other parameters */ + /* set memory map pointers */ + port->bmi_regs = base_addr + BMI_PORT_REGS_OFFSET; + port->qmi_regs = base_addr + QMI_PORT_REGS_OFFSET; + port->hwp_regs = base_addr + HWP_PORT_REGS_OFFSET; + + port->max_frame_length = DFLT_PORT_MAX_FRAME_LENGTH; + /* resource distribution. */ + + port->fifo_bufs.num = + get_dflt_num_of_fifo_bufs(port->rev_info.major, port->port_type, + port->port_speed) * FMAN_BMI_FIFO_UNITS; + port->fifo_bufs.extra = + DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS * FMAN_BMI_FIFO_UNITS; + + port->open_dmas.num = + get_dflt_num_of_open_dmas(port->rev_info.major, + port->port_type, port->port_speed); + port->open_dmas.extra = + get_dflt_extra_num_of_open_dmas(port->rev_info.major, + port->port_type, port->port_speed); + port->tasks.num = + get_dflt_num_of_tasks(port->rev_info.major, + port->port_type, port->port_speed); + port->tasks.extra = + get_dflt_extra_num_of_tasks(port->rev_info.major, + port->port_type, port->port_speed); + + /* FM_HEAVY_TRAFFIC_SEQUENCER_HANG_ERRATA_FMAN_A006981 errata + * workaround + */ + if ((port->rev_info.major == 6) && (port->rev_info.minor == 0) && + (((port->port_type == FMAN_PORT_TYPE_TX) && + (port->port_speed == 1000)))) { + port->open_dmas.num = 16; + port->open_dmas.extra = 0; + } + + if (port->rev_info.major >= 6 && + port->port_type == FMAN_PORT_TYPE_TX && + port->port_speed == 1000) { + /* FM_WRONG_RESET_VALUES_ERRATA_FMAN_A005127 Errata + * workaround + */ + u32 reg; + + reg = 0x00001013; + iowrite32be(reg, &port->bmi_regs->tx.fmbm_tfp); + } + + return 0; + +err_port_cfg: + kfree(port->cfg); + return -EINVAL; +} +EXPORT_SYMBOL(fman_port_config); + +/* + * fman_port_use_kg_hash + * @port: A pointer to a FM Port module. + * @enable: enable or disable + * + * Sets the HW KeyGen or the BMI as HW Parser next engine, enabling + * or bypassing the KeyGen hashing of Rx traffic + */ +void fman_port_use_kg_hash(struct fman_port *port, bool enable) +{ + if (enable) + /* After the Parser frames go to KeyGen */ + iowrite32be(NIA_ENG_HWK, &port->bmi_regs->rx.fmbm_rfpne); + else + /* After the Parser frames go to BMI */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME, + &port->bmi_regs->rx.fmbm_rfpne); +} +EXPORT_SYMBOL(fman_port_use_kg_hash); + +/** + * fman_port_init + * @port: A pointer to a FM Port module. + * + * Initializes the FM PORT module by defining the software structure and + * configuring the hardware registers. + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_init(struct fman_port *port) +{ + struct fman_port_init_params params; + struct fman_keygen *keygen; + struct fman_port_cfg *cfg; + int err; + + if (is_init_done(port->cfg)) + return -EINVAL; + + err = fman_sp_build_buffer_struct(&port->cfg->int_context, + &port->cfg->buffer_prefix_content, + &port->cfg->buf_margins, + &port->buffer_offsets, + &port->internal_buf_offset); + if (err) + return err; + + cfg = port->cfg; + + if (port->port_type == FMAN_PORT_TYPE_RX) { + /* Call the external Buffer routine which also checks fifo + * size and updates it if necessary + */ + /* define external buffer pools and pool depletion */ + err = set_ext_buffer_pools(port); + if (err) + return err; + /* check if the largest external buffer pool is large enough */ + if (cfg->buf_margins.start_margins + MIN_EXT_BUF_SIZE + + cfg->buf_margins.end_margins > + port->rx_pools_params.largest_buf_size) { + dev_err(port->dev, "%s: buf_margins.start_margins (%d) + minimum buf size (64) + buf_margins.end_margins (%d) is larger than maximum external buffer size (%d)\n", + __func__, cfg->buf_margins.start_margins, + cfg->buf_margins.end_margins, + port->rx_pools_params.largest_buf_size); + return -EINVAL; + } + } + + /* Call FM module routine for communicating parameters */ + memset(¶ms, 0, sizeof(params)); + params.port_id = port->port_id; + params.port_type = port->port_type; + params.port_speed = port->port_speed; + params.num_of_tasks = (u8)port->tasks.num; + params.num_of_extra_tasks = (u8)port->tasks.extra; + params.num_of_open_dmas = (u8)port->open_dmas.num; + params.num_of_extra_open_dmas = (u8)port->open_dmas.extra; + + if (port->fifo_bufs.num) { + err = verify_size_of_fifo(port); + if (err) + return err; + } + params.size_of_fifo = port->fifo_bufs.num; + params.extra_size_of_fifo = port->fifo_bufs.extra; + params.deq_pipeline_depth = port->cfg->tx_fifo_deq_pipeline_depth; + params.max_frame_length = port->max_frame_length; + + err = fman_set_port_params(port->fm, ¶ms); + if (err) + return err; + + err = init_low_level_driver(port); + if (err) + return err; + + if (port->cfg->pcd_fqs_count) { + keygen = port->dts_params.fman->keygen; + err = keygen_port_hashing_init(keygen, port->port_id, + port->cfg->pcd_base_fqid, + port->cfg->pcd_fqs_count); + if (err) + return err; + + fman_port_use_kg_hash(port, true); + } + + kfree(port->cfg); + port->cfg = NULL; + + return 0; +} +EXPORT_SYMBOL(fman_port_init); + +/** + * fman_port_cfg_buf_prefix_content + * @port: A pointer to a FM Port module. + * @buffer_prefix_content: A structure of parameters describing + * the structure of the buffer. + * Out parameter: + * Start margin - offset of data from + * start of external buffer. + * Defines the structure, size and content of the application buffer. + * The prefix, in Tx ports, if 'pass_prs_result', the application should set + * a value to their offsets in the prefix of the FM will save the first + * 'priv_data_size', than, depending on 'pass_prs_result' and + * 'pass_time_stamp', copy parse result and timeStamp, and the packet itself + * (in this order), to the application buffer, and to offset. + * Calling this routine changes the buffer margins definitions in the internal + * driver data base from its default configuration: + * Data size: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE] + * Pass Parser result: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT]. + * Pass timestamp: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP]. + * May be used for all ports + * + * Allowed only following fman_port_config() and before fman_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_cfg_buf_prefix_content(struct fman_port *port, + struct fman_buffer_prefix_content * + buffer_prefix_content) +{ + if (is_init_done(port->cfg)) + return -EINVAL; + + memcpy(&port->cfg->buffer_prefix_content, + buffer_prefix_content, + sizeof(struct fman_buffer_prefix_content)); + /* if data_align was not initialized by user, + * we return to driver's default + */ + if (!port->cfg->buffer_prefix_content.data_align) + port->cfg->buffer_prefix_content.data_align = + DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN; + + return 0; +} +EXPORT_SYMBOL(fman_port_cfg_buf_prefix_content); + +/** + * fman_port_disable + * @port: A pointer to a FM Port module. + * + * Gracefully disable an FM port. The port will not start new tasks after all + * tasks associated with the port are terminated. + * + * This is a blocking routine, it returns after port is gracefully stopped, + * i.e. the port will not except new frames, but it will finish all frames + * or tasks which were already began. + * Allowed only following fman_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_disable(struct fman_port *port) +{ + u32 __iomem *bmi_cfg_reg, *bmi_status_reg; + u32 tmp; + bool rx_port, failure = false; + int count; + + if (!is_init_done(port->cfg)) + return -EINVAL; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg; + bmi_status_reg = &port->bmi_regs->rx.fmbm_rst; + rx_port = true; + break; + case FMAN_PORT_TYPE_TX: + bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg; + bmi_status_reg = &port->bmi_regs->tx.fmbm_tst; + rx_port = false; + break; + default: + return -EINVAL; + } + + /* Disable QMI */ + if (!rx_port) { + tmp = ioread32be(&port->qmi_regs->fmqm_pnc) & ~QMI_PORT_CFG_EN; + iowrite32be(tmp, &port->qmi_regs->fmqm_pnc); + + /* Wait for QMI to finish FD handling */ + count = 100; + do { + udelay(10); + tmp = ioread32be(&port->qmi_regs->fmqm_pns); + } while ((tmp & QMI_PORT_STATUS_DEQ_FD_BSY) && --count); + + if (count == 0) { + /* Timeout */ + failure = true; + } + } + + /* Disable BMI */ + tmp = ioread32be(bmi_cfg_reg) & ~BMI_PORT_CFG_EN; + iowrite32be(tmp, bmi_cfg_reg); + + /* Wait for graceful stop end */ + count = 500; + do { + udelay(10); + tmp = ioread32be(bmi_status_reg); + } while ((tmp & BMI_PORT_STATUS_BSY) && --count); + + if (count == 0) { + /* Timeout */ + failure = true; + } + + if (failure) + dev_dbg(port->dev, "%s: FMan Port[%d]: BMI or QMI is Busy. Port forced down\n", + __func__, port->port_id); + + return 0; +} +EXPORT_SYMBOL(fman_port_disable); + +/** + * fman_port_enable + * @port: A pointer to a FM Port module. + * + * A runtime routine provided to allow disable/enable of port. + * + * Allowed only following fman_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_enable(struct fman_port *port) +{ + u32 __iomem *bmi_cfg_reg; + u32 tmp; + bool rx_port; + + if (!is_init_done(port->cfg)) + return -EINVAL; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg; + rx_port = true; + break; + case FMAN_PORT_TYPE_TX: + bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg; + rx_port = false; + break; + default: + return -EINVAL; + } + + /* Enable QMI */ + if (!rx_port) { + tmp = ioread32be(&port->qmi_regs->fmqm_pnc) | QMI_PORT_CFG_EN; + iowrite32be(tmp, &port->qmi_regs->fmqm_pnc); + } + + /* Enable BMI */ + tmp = ioread32be(bmi_cfg_reg) | BMI_PORT_CFG_EN; + iowrite32be(tmp, bmi_cfg_reg); + + return 0; +} +EXPORT_SYMBOL(fman_port_enable); + +/** + * fman_port_bind + * @dev: FMan Port OF device pointer + * + * Bind to a specific FMan Port. + * + * Allowed only after the port was created. + * + * Return: A pointer to the FMan port device. + */ +struct fman_port *fman_port_bind(struct device *dev) +{ + return (struct fman_port *)(dev_get_drvdata(get_device(dev))); +} +EXPORT_SYMBOL(fman_port_bind); + +/** + * fman_port_get_qman_channel_id + * @port: Pointer to the FMan port devuce + * + * Get the QMan channel ID for the specific port + * + * Return: QMan channel ID + */ +u32 fman_port_get_qman_channel_id(struct fman_port *port) +{ + return port->dts_params.qman_channel_id; +} +EXPORT_SYMBOL(fman_port_get_qman_channel_id); + +/** + * fman_port_get_device + * @port: Pointer to the FMan port device + * + * Get the 'struct device' associated to the specified FMan port device + * + * Return: pointer to associated 'struct device' + */ +struct device *fman_port_get_device(struct fman_port *port) +{ + return port->dev; +} +EXPORT_SYMBOL(fman_port_get_device); + +int fman_port_get_hash_result_offset(struct fman_port *port, u32 *offset) +{ + if (port->buffer_offsets.hash_result_offset == ILLEGAL_BASE) + return -EINVAL; + + *offset = port->buffer_offsets.hash_result_offset; + + return 0; +} +EXPORT_SYMBOL(fman_port_get_hash_result_offset); + +int fman_port_get_tstamp(struct fman_port *port, const void *data, u64 *tstamp) +{ + if (port->buffer_offsets.time_stamp_offset == ILLEGAL_BASE) + return -EINVAL; + + *tstamp = be64_to_cpu(*(__be64 *)(data + + port->buffer_offsets.time_stamp_offset)); + + return 0; +} +EXPORT_SYMBOL(fman_port_get_tstamp); + +static int fman_port_probe(struct platform_device *of_dev) +{ + struct fman_port *port; + struct fman *fman; + struct device_node *fm_node, *port_node; + struct platform_device *fm_pdev; + struct resource res; + struct resource *dev_res; + u32 val; + int err = 0, lenp; + enum fman_port_type port_type; + u16 port_speed; + u8 port_id; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->dev = &of_dev->dev; + + port_node = of_node_get(of_dev->dev.of_node); + + /* Get the FM node */ + fm_node = of_get_parent(port_node); + if (!fm_node) { + dev_err(port->dev, "%s: of_get_parent() failed\n", __func__); + err = -ENODEV; + goto return_err; + } + + fm_pdev = of_find_device_by_node(fm_node); + of_node_put(fm_node); + if (!fm_pdev) { + err = -EINVAL; + goto return_err; + } + + fman = dev_get_drvdata(&fm_pdev->dev); + if (!fman) { + err = -EINVAL; + goto put_device; + } + + err = of_property_read_u32(port_node, "cell-index", &val); + if (err) { + dev_err(port->dev, "%s: reading cell-index for %pOF failed\n", + __func__, port_node); + err = -EINVAL; + goto put_device; + } + port_id = (u8)val; + port->dts_params.id = port_id; + + if (of_device_is_compatible(port_node, "fsl,fman-v3-port-tx")) { + port_type = FMAN_PORT_TYPE_TX; + port_speed = 1000; + if (of_find_property(port_node, "fsl,fman-10g-port", &lenp)) + port_speed = 10000; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-tx")) { + if (port_id >= TX_10G_PORT_BASE) + port_speed = 10000; + else + port_speed = 1000; + port_type = FMAN_PORT_TYPE_TX; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v3-port-rx")) { + port_type = FMAN_PORT_TYPE_RX; + port_speed = 1000; + if (of_find_property(port_node, "fsl,fman-10g-port", &lenp)) + port_speed = 10000; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-rx")) { + if (port_id >= RX_10G_PORT_BASE) + port_speed = 10000; + else + port_speed = 1000; + port_type = FMAN_PORT_TYPE_RX; + + } else { + dev_err(port->dev, "%s: Illegal port type\n", __func__); + err = -EINVAL; + goto put_device; + } + + port->dts_params.type = port_type; + port->dts_params.speed = port_speed; + + if (port_type == FMAN_PORT_TYPE_TX) { + u32 qman_channel_id; + + qman_channel_id = fman_get_qman_channel_id(fman, port_id); + if (qman_channel_id == 0) { + dev_err(port->dev, "%s: incorrect qman-channel-id\n", + __func__); + err = -EINVAL; + goto put_device; + } + port->dts_params.qman_channel_id = qman_channel_id; + } + + err = of_address_to_resource(port_node, 0, &res); + if (err < 0) { + dev_err(port->dev, "%s: of_address_to_resource() failed\n", + __func__); + err = -ENOMEM; + goto put_device; + } + + port->dts_params.fman = fman; + + of_node_put(port_node); + + dev_res = __devm_request_region(port->dev, &res, res.start, + resource_size(&res), "fman-port"); + if (!dev_res) { + dev_err(port->dev, "%s: __devm_request_region() failed\n", + __func__); + err = -EINVAL; + goto free_port; + } + + port->dts_params.base_addr = devm_ioremap(port->dev, res.start, + resource_size(&res)); + if (!port->dts_params.base_addr) + dev_err(port->dev, "%s: devm_ioremap() failed\n", __func__); + + dev_set_drvdata(&of_dev->dev, port); + + return 0; + +put_device: + put_device(&fm_pdev->dev); +return_err: + of_node_put(port_node); +free_port: + kfree(port); + return err; +} + +static const struct of_device_id fman_port_match[] = { + {.compatible = "fsl,fman-v3-port-rx"}, + {.compatible = "fsl,fman-v2-port-rx"}, + {.compatible = "fsl,fman-v3-port-tx"}, + {.compatible = "fsl,fman-v2-port-tx"}, + {} +}; + +MODULE_DEVICE_TABLE(of, fman_port_match); + +static struct platform_driver fman_port_driver = { + .driver = { + .name = "fsl-fman-port", + .of_match_table = fman_port_match, + }, + .probe = fman_port_probe, +}; + +static int __init fman_port_load(void) +{ + int err; + + pr_debug("FSL DPAA FMan driver\n"); + + err = platform_driver_register(&fman_port_driver); + if (err < 0) + pr_err("Error, platform_driver_register() = %d\n", err); + + return err; +} +module_init(fman_port_load); + +static void __exit fman_port_unload(void) +{ + platform_driver_unregister(&fman_port_driver); +} +module_exit(fman_port_unload); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Freescale DPAA Frame Manager Port driver"); diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h new file mode 100644 index 000000000..82f12661a --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -0,0 +1,162 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FMAN_PORT_H +#define __FMAN_PORT_H + +#include "fman.h" + +/* FM Port API + * The FM uses a general module called "port" to represent a Tx port (MAC), + * an Rx port (MAC). + * The number of ports in an FM varies between SOCs. + * The SW driver manages these ports as sub-modules of the FM,i.e. after an + * FM is initialized, its ports may be initialized and operated upon. + * The port is initialized aware of its type, but other functions on a port + * may be indifferent to its type. When necessary, the driver verifies + * coherence and returns error if applicable. + * On initialization, user specifies the port type and it's index (relative + * to the port's type) - always starting at 0. + */ + +/* FM Frame error */ +/* Frame Descriptor errors */ +/* Not for Rx-Port! Unsupported Format */ +#define FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT FM_FD_ERR_UNSUPPORTED_FORMAT +/* Not for Rx-Port! Length Error */ +#define FM_PORT_FRM_ERR_LENGTH FM_FD_ERR_LENGTH +/* DMA Data error */ +#define FM_PORT_FRM_ERR_DMA FM_FD_ERR_DMA +/* non Frame-Manager error; probably come from SEC that was chained to FM */ +#define FM_PORT_FRM_ERR_NON_FM FM_FD_RX_STATUS_ERR_NON_FM + /* IPR error */ +#define FM_PORT_FRM_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR) +/* IPR non-consistent-sp */ +#define FM_PORT_FRM_ERR_IPR_NCSP (FM_FD_ERR_IPR_NCSP & \ + ~FM_FD_IPR) + +/* Rx FIFO overflow, FCS error, code error, running disparity + * error (SGMII and TBI modes), FIFO parity error. + * PHY Sequence error, PHY error control character detected. + */ +#define FM_PORT_FRM_ERR_PHYSICAL FM_FD_ERR_PHYSICAL +/* Frame too long OR Frame size exceeds max_length_frame */ +#define FM_PORT_FRM_ERR_SIZE FM_FD_ERR_SIZE +/* indicates a classifier "drop" operation */ +#define FM_PORT_FRM_ERR_CLS_DISCARD FM_FD_ERR_CLS_DISCARD +/* Extract Out of Frame */ +#define FM_PORT_FRM_ERR_EXTRACTION FM_FD_ERR_EXTRACTION +/* No Scheme Selected */ +#define FM_PORT_FRM_ERR_NO_SCHEME FM_FD_ERR_NO_SCHEME +/* Keysize Overflow */ +#define FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW FM_FD_ERR_KEYSIZE_OVERFLOW +/* Frame color is red */ +#define FM_PORT_FRM_ERR_COLOR_RED FM_FD_ERR_COLOR_RED +/* Frame color is yellow */ +#define FM_PORT_FRM_ERR_COLOR_YELLOW FM_FD_ERR_COLOR_YELLOW +/* Parser Time out Exceed */ +#define FM_PORT_FRM_ERR_PRS_TIMEOUT FM_FD_ERR_PRS_TIMEOUT +/* Invalid Soft Parser instruction */ +#define FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT FM_FD_ERR_PRS_ILL_INSTRUCT +/* Header error was identified during parsing */ +#define FM_PORT_FRM_ERR_PRS_HDR_ERR FM_FD_ERR_PRS_HDR_ERR +/* Frame parsed beyind 256 first bytes */ +#define FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED FM_FD_ERR_BLOCK_LIMIT_EXCEEDED +/* FPM Frame Processing Timeout Exceeded */ +#define FM_PORT_FRM_ERR_PROCESS_TIMEOUT 0x00000001 + +struct fman_port; + +/* A structure for additional Rx port parameters */ +struct fman_port_rx_params { + u32 err_fqid; /* Error Queue Id. */ + u32 dflt_fqid; /* Default Queue Id. */ + u32 pcd_base_fqid; /* PCD base Queue Id. */ + u32 pcd_fqs_count; /* Number of PCD FQs. */ + + /* Which external buffer pools are used + * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes. + */ + struct fman_ext_pools ext_buf_pools; +}; + +/* A structure for additional non-Rx port parameters */ +struct fman_port_non_rx_params { + /* Error Queue Id. */ + u32 err_fqid; + /* For Tx - Default Confirmation queue, 0 means no Tx confirmation + * for processed frames. For OP port - default Rx queue. + */ + u32 dflt_fqid; +}; + +/* A union for additional parameters depending on port type */ +union fman_port_specific_params { + /* Rx port parameters structure */ + struct fman_port_rx_params rx_params; + /* Non-Rx port parameters structure */ + struct fman_port_non_rx_params non_rx_params; +}; + +/* A structure representing FM initialization parameters */ +struct fman_port_params { + /* Virtual Address of memory mapped FM Port registers. */ + void *fm; + union fman_port_specific_params specific_params; + /* Additional parameters depending on port type. */ +}; + +int fman_port_config(struct fman_port *port, struct fman_port_params *params); + +void fman_port_use_kg_hash(struct fman_port *port, bool enable); + +int fman_port_init(struct fman_port *port); + +int fman_port_cfg_buf_prefix_content(struct fman_port *port, + struct fman_buffer_prefix_content + *buffer_prefix_content); + +int fman_port_disable(struct fman_port *port); + +int fman_port_enable(struct fman_port *port); + +u32 fman_port_get_qman_channel_id(struct fman_port *port); + +int fman_port_get_hash_result_offset(struct fman_port *port, u32 *offset); + +int fman_port_get_tstamp(struct fman_port *port, const void *data, u64 *tstamp); + +struct fman_port *fman_port_bind(struct device *dev); + +struct device *fman_port_get_device(struct fman_port *port); + +#endif /* __FMAN_PORT_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.c b/drivers/net/ethernet/freescale/fman/fman_sp.c new file mode 100644 index 000000000..248f5bcca --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_sp.c @@ -0,0 +1,169 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 "fman_sp.h" +#include "fman.h" + +void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools + *fm_ext_pools, + u8 *ordered_array, + u16 *sizes_array) +{ + u16 buf_size = 0; + int i = 0, j = 0, k = 0; + + /* First we copy the external buffers pools information + * to an ordered local array + */ + for (i = 0; i < fm_ext_pools->num_of_pools_used; i++) { + /* get pool size */ + buf_size = fm_ext_pools->ext_buf_pool[i].size; + + /* keep sizes in an array according to poolId + * for direct access + */ + sizes_array[fm_ext_pools->ext_buf_pool[i].id] = buf_size; + + /* save poolId in an ordered array according to size */ + for (j = 0; j <= i; j++) { + /* this is the next free place in the array */ + if (j == i) + ordered_array[i] = + fm_ext_pools->ext_buf_pool[i].id; + else { + /* find the right place for this poolId */ + if (buf_size < sizes_array[ordered_array[j]]) { + /* move the pool_ids one place ahead + * to make room for this poolId + */ + for (k = i; k > j; k--) + ordered_array[k] = + ordered_array[k - 1]; + + /* now k==j, this is the place for + * the new size + */ + ordered_array[k] = + fm_ext_pools->ext_buf_pool[i].id; + break; + } + } + } + } +} +EXPORT_SYMBOL(fman_sp_set_buf_pools_in_asc_order_of_buf_sizes); + +int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy * + int_context_data_copy, + struct fman_buffer_prefix_content * + buffer_prefix_content, + struct fman_sp_buf_margins *buf_margins, + struct fman_sp_buffer_offsets *buffer_offsets, + u8 *internal_buf_offset) +{ + u32 tmp; + + /* Align start of internal context data to 16 byte */ + int_context_data_copy->ext_buf_offset = (u16) + ((buffer_prefix_content->priv_data_size & (OFFSET_UNITS - 1)) ? + ((buffer_prefix_content->priv_data_size + OFFSET_UNITS) & + ~(u16)(OFFSET_UNITS - 1)) : + buffer_prefix_content->priv_data_size); + + /* Translate margin and int_context params to FM parameters */ + /* Initialize with illegal value. Later we'll set legal values. */ + buffer_offsets->prs_result_offset = (u32)ILLEGAL_BASE; + buffer_offsets->time_stamp_offset = (u32)ILLEGAL_BASE; + buffer_offsets->hash_result_offset = (u32)ILLEGAL_BASE; + + /* Internally the driver supports 4 options + * 1. prsResult/timestamp/hashResult selection (in fact 8 options, + * but for simplicity we'll + * relate to it as 1). + * 2. All IC context (from AD) not including debug. + */ + + /* This case covers the options under 1 */ + /* Copy size must be in 16-byte granularity. */ + int_context_data_copy->size = + (u16)((buffer_prefix_content->pass_prs_result ? 32 : 0) + + ((buffer_prefix_content->pass_time_stamp || + buffer_prefix_content->pass_hash_result) ? 16 : 0)); + + /* Align start of internal context data to 16 byte */ + int_context_data_copy->int_context_offset = + (u8)(buffer_prefix_content->pass_prs_result ? 32 : + ((buffer_prefix_content->pass_time_stamp || + buffer_prefix_content->pass_hash_result) ? 64 : 0)); + + if (buffer_prefix_content->pass_prs_result) + buffer_offsets->prs_result_offset = + int_context_data_copy->ext_buf_offset; + if (buffer_prefix_content->pass_time_stamp) + buffer_offsets->time_stamp_offset = + buffer_prefix_content->pass_prs_result ? + (int_context_data_copy->ext_buf_offset + + sizeof(struct fman_prs_result)) : + int_context_data_copy->ext_buf_offset; + if (buffer_prefix_content->pass_hash_result) + /* If PR is not requested, whether TS is + * requested or not, IC will be copied from TS + */ + buffer_offsets->hash_result_offset = + buffer_prefix_content->pass_prs_result ? + (int_context_data_copy->ext_buf_offset + + sizeof(struct fman_prs_result) + 8) : + int_context_data_copy->ext_buf_offset + 8; + + if (int_context_data_copy->size) + buf_margins->start_margins = + (u16)(int_context_data_copy->ext_buf_offset + + int_context_data_copy->size); + else + /* No Internal Context passing, STartMargin is + * immediately after private_info + */ + buf_margins->start_margins = + buffer_prefix_content->priv_data_size; + + /* align data start */ + tmp = (u32)(buf_margins->start_margins % + buffer_prefix_content->data_align); + if (tmp) + buf_margins->start_margins += + (buffer_prefix_content->data_align - tmp); + buffer_offsets->data_offset = buf_margins->start_margins; + + return 0; +} +EXPORT_SYMBOL(fman_sp_build_buffer_struct); + diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.h b/drivers/net/ethernet/freescale/fman/fman_sp.h new file mode 100644 index 000000000..820b7f630 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_sp.h @@ -0,0 +1,103 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __FM_SP_H +#define __FM_SP_H + +#include "fman.h" +#include <linux/types.h> + +#define ILLEGAL_BASE (~0) + +/* defaults */ +#define DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN 64 + +/* Registers bit fields */ +#define FMAN_SP_EXT_BUF_POOL_EN_COUNTER 0x40000000 +#define FMAN_SP_EXT_BUF_POOL_VALID 0x80000000 +#define FMAN_SP_EXT_BUF_POOL_BACKUP 0x20000000 +#define FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE 0x00100000 +#define FMAN_SP_SG_DISABLE 0x80000000 + +/* shifts */ +#define FMAN_SP_EXT_BUF_MARG_START_SHIFT 16 +#define FMAN_SP_DMA_ATTR_SWP_SHIFT 30 +#define FMAN_SP_IC_TO_EXT_SHIFT 16 +#define FMAN_SP_IC_FROM_INT_SHIFT 8 + +/* structure for defining internal context copying */ +struct fman_sp_int_context_data_copy { + /* < Offset in External buffer to which internal + * context is copied to (Rx) or taken from (Tx, Op). + */ + u16 ext_buf_offset; + /* Offset within internal context to copy from + * (Rx) or to copy to (Tx, Op). + */ + u8 int_context_offset; + /* Internal offset size to be copied */ + u16 size; +}; + +/* struct for defining external buffer margins */ +struct fman_sp_buf_margins { + /* Number of bytes to be left at the beginning + * of the external buffer (must be divisible by 16) + */ + u16 start_margins; + /* number of bytes to be left at the end + * of the external buffer(must be divisible by 16) + */ + u16 end_margins; +}; + +struct fman_sp_buffer_offsets { + u32 data_offset; + u32 prs_result_offset; + u32 time_stamp_offset; + u32 hash_result_offset; +}; + +int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy + *int_context_data_copy, + struct fman_buffer_prefix_content + *buffer_prefix_content, + struct fman_sp_buf_margins *buf_margins, + struct fman_sp_buffer_offsets + *buffer_offsets, + u8 *internal_buf_offset); + +void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools + *fm_ext_pools, + u8 *ordered_array, + u16 *sizes_array); + +#endif /* __FM_SP_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c new file mode 100644 index 000000000..41946b16f --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -0,0 +1,829 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_tgec.h" +#include "fman.h" + +#include <linux/slab.h> +#include <linux/bitrev.h> +#include <linux/io.h> +#include <linux/crc32.h> + +/* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */ +#define TGEC_TX_IPG_LENGTH_MASK 0x000003ff + +/* Command and Configuration Register (COMMAND_CONFIG) */ +#define CMD_CFG_EN_TIMESTAMP 0x00100000 +#define CMD_CFG_NO_LEN_CHK 0x00020000 +#define CMD_CFG_PAUSE_IGNORE 0x00000100 +#define CMF_CFG_CRC_FWD 0x00000040 +#define CMD_CFG_PROMIS_EN 0x00000010 +#define CMD_CFG_RX_EN 0x00000002 +#define CMD_CFG_TX_EN 0x00000001 + +/* Interrupt Mask Register (IMASK) */ +#define TGEC_IMASK_MDIO_SCAN_EVENT 0x00010000 +#define TGEC_IMASK_MDIO_CMD_CMPL 0x00008000 +#define TGEC_IMASK_REM_FAULT 0x00004000 +#define TGEC_IMASK_LOC_FAULT 0x00002000 +#define TGEC_IMASK_TX_ECC_ER 0x00001000 +#define TGEC_IMASK_TX_FIFO_UNFL 0x00000800 +#define TGEC_IMASK_TX_FIFO_OVFL 0x00000400 +#define TGEC_IMASK_TX_ER 0x00000200 +#define TGEC_IMASK_RX_FIFO_OVFL 0x00000100 +#define TGEC_IMASK_RX_ECC_ER 0x00000080 +#define TGEC_IMASK_RX_JAB_FRM 0x00000040 +#define TGEC_IMASK_RX_OVRSZ_FRM 0x00000020 +#define TGEC_IMASK_RX_RUNT_FRM 0x00000010 +#define TGEC_IMASK_RX_FRAG_FRM 0x00000008 +#define TGEC_IMASK_RX_LEN_ER 0x00000004 +#define TGEC_IMASK_RX_CRC_ER 0x00000002 +#define TGEC_IMASK_RX_ALIGN_ER 0x00000001 + +/* Hashtable Control Register (HASHTABLE_CTRL) */ +#define TGEC_HASH_MCAST_SHIFT 23 +#define TGEC_HASH_MCAST_EN 0x00000200 +#define TGEC_HASH_ADR_MSK 0x000001ff + +#define DEFAULT_TX_IPG_LENGTH 12 +#define DEFAULT_MAX_FRAME_LENGTH 0x600 +#define DEFAULT_PAUSE_QUANT 0xf000 + +/* number of pattern match registers (entries) */ +#define TGEC_NUM_OF_PADDRS 1 + +/* Group address bit indication */ +#define GROUP_ADDRESS 0x0000010000000000LL + +/* Hash table size (= 32 bits*8 regs) */ +#define TGEC_HASH_TABLE_SIZE 512 + +/* tGEC memory map */ +struct tgec_regs { + u32 tgec_id; /* 0x000 Controller ID */ + u32 reserved001[1]; /* 0x004 */ + u32 command_config; /* 0x008 Control and configuration */ + u32 mac_addr_0; /* 0x00c Lower 32 bits of the MAC adr */ + u32 mac_addr_1; /* 0x010 Upper 16 bits of the MAC adr */ + u32 maxfrm; /* 0x014 Maximum frame length */ + u32 pause_quant; /* 0x018 Pause quanta */ + u32 rx_fifo_sections; /* 0x01c */ + u32 tx_fifo_sections; /* 0x020 */ + u32 rx_fifo_almost_f_e; /* 0x024 */ + u32 tx_fifo_almost_f_e; /* 0x028 */ + u32 hashtable_ctrl; /* 0x02c Hash table control */ + u32 mdio_cfg_status; /* 0x030 */ + u32 mdio_command; /* 0x034 */ + u32 mdio_data; /* 0x038 */ + u32 mdio_regaddr; /* 0x03c */ + u32 status; /* 0x040 */ + u32 tx_ipg_len; /* 0x044 Transmitter inter-packet-gap */ + u32 mac_addr_2; /* 0x048 Lower 32 bits of 2nd MAC adr */ + u32 mac_addr_3; /* 0x04c Upper 16 bits of 2nd MAC adr */ + u32 rx_fifo_ptr_rd; /* 0x050 */ + u32 rx_fifo_ptr_wr; /* 0x054 */ + u32 tx_fifo_ptr_rd; /* 0x058 */ + u32 tx_fifo_ptr_wr; /* 0x05c */ + u32 imask; /* 0x060 Interrupt mask */ + u32 ievent; /* 0x064 Interrupt event */ + u32 udp_port; /* 0x068 Defines a UDP Port number */ + u32 type_1588v2; /* 0x06c Type field for 1588v2 */ + u32 reserved070[4]; /* 0x070 */ + /* 10Ge Statistics Counter */ + u32 tfrm_u; /* 80 aFramesTransmittedOK */ + u32 tfrm_l; /* 84 aFramesTransmittedOK */ + u32 rfrm_u; /* 88 aFramesReceivedOK */ + u32 rfrm_l; /* 8c aFramesReceivedOK */ + u32 rfcs_u; /* 90 aFrameCheckSequenceErrors */ + u32 rfcs_l; /* 94 aFrameCheckSequenceErrors */ + u32 raln_u; /* 98 aAlignmentErrors */ + u32 raln_l; /* 9c aAlignmentErrors */ + u32 txpf_u; /* A0 aPAUSEMACCtrlFramesTransmitted */ + u32 txpf_l; /* A4 aPAUSEMACCtrlFramesTransmitted */ + u32 rxpf_u; /* A8 aPAUSEMACCtrlFramesReceived */ + u32 rxpf_l; /* Ac aPAUSEMACCtrlFramesReceived */ + u32 rlong_u; /* B0 aFrameTooLongErrors */ + u32 rlong_l; /* B4 aFrameTooLongErrors */ + u32 rflr_u; /* B8 aInRangeLengthErrors */ + u32 rflr_l; /* Bc aInRangeLengthErrors */ + u32 tvlan_u; /* C0 VLANTransmittedOK */ + u32 tvlan_l; /* C4 VLANTransmittedOK */ + u32 rvlan_u; /* C8 VLANReceivedOK */ + u32 rvlan_l; /* Cc VLANReceivedOK */ + u32 toct_u; /* D0 if_out_octets */ + u32 toct_l; /* D4 if_out_octets */ + u32 roct_u; /* D8 if_in_octets */ + u32 roct_l; /* Dc if_in_octets */ + u32 ruca_u; /* E0 if_in_ucast_pkts */ + u32 ruca_l; /* E4 if_in_ucast_pkts */ + u32 rmca_u; /* E8 ifInMulticastPkts */ + u32 rmca_l; /* Ec ifInMulticastPkts */ + u32 rbca_u; /* F0 ifInBroadcastPkts */ + u32 rbca_l; /* F4 ifInBroadcastPkts */ + u32 terr_u; /* F8 if_out_errors */ + u32 terr_l; /* Fc if_out_errors */ + u32 reserved100[2]; /* 100-108 */ + u32 tuca_u; /* 108 if_out_ucast_pkts */ + u32 tuca_l; /* 10c if_out_ucast_pkts */ + u32 tmca_u; /* 110 ifOutMulticastPkts */ + u32 tmca_l; /* 114 ifOutMulticastPkts */ + u32 tbca_u; /* 118 ifOutBroadcastPkts */ + u32 tbca_l; /* 11c ifOutBroadcastPkts */ + u32 rdrp_u; /* 120 etherStatsDropEvents */ + u32 rdrp_l; /* 124 etherStatsDropEvents */ + u32 reoct_u; /* 128 etherStatsOctets */ + u32 reoct_l; /* 12c etherStatsOctets */ + u32 rpkt_u; /* 130 etherStatsPkts */ + u32 rpkt_l; /* 134 etherStatsPkts */ + u32 trund_u; /* 138 etherStatsUndersizePkts */ + u32 trund_l; /* 13c etherStatsUndersizePkts */ + u32 r64_u; /* 140 etherStatsPkts64Octets */ + u32 r64_l; /* 144 etherStatsPkts64Octets */ + u32 r127_u; /* 148 etherStatsPkts65to127Octets */ + u32 r127_l; /* 14c etherStatsPkts65to127Octets */ + u32 r255_u; /* 150 etherStatsPkts128to255Octets */ + u32 r255_l; /* 154 etherStatsPkts128to255Octets */ + u32 r511_u; /* 158 etherStatsPkts256to511Octets */ + u32 r511_l; /* 15c etherStatsPkts256to511Octets */ + u32 r1023_u; /* 160 etherStatsPkts512to1023Octets */ + u32 r1023_l; /* 164 etherStatsPkts512to1023Octets */ + u32 r1518_u; /* 168 etherStatsPkts1024to1518Octets */ + u32 r1518_l; /* 16c etherStatsPkts1024to1518Octets */ + u32 r1519x_u; /* 170 etherStatsPkts1519toX */ + u32 r1519x_l; /* 174 etherStatsPkts1519toX */ + u32 trovr_u; /* 178 etherStatsOversizePkts */ + u32 trovr_l; /* 17c etherStatsOversizePkts */ + u32 trjbr_u; /* 180 etherStatsJabbers */ + u32 trjbr_l; /* 184 etherStatsJabbers */ + u32 trfrg_u; /* 188 etherStatsFragments */ + u32 trfrg_l; /* 18C etherStatsFragments */ + u32 rerr_u; /* 190 if_in_errors */ + u32 rerr_l; /* 194 if_in_errors */ +}; + +struct tgec_cfg { + bool pause_ignore; + bool promiscuous_mode_enable; + u16 max_frame_length; + u16 pause_quant; + u32 tx_ipg_length; +}; + +struct fman_mac { + /* Pointer to the memory mapped registers. */ + struct tgec_regs __iomem *regs; + /* MAC address of device; */ + u64 addr; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + struct tgec_cfg *cfg; + void *fm; + struct fman_rev_info fm_rev_info; + bool allmulti_enabled; +}; + +static void set_mac_address(struct tgec_regs __iomem *regs, u8 *adr) +{ + u32 tmp0, tmp1; + + tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); + tmp1 = (u32)(adr[4] | adr[5] << 8); + iowrite32be(tmp0, ®s->mac_addr_0); + iowrite32be(tmp1, ®s->mac_addr_1); +} + +static void set_dflts(struct tgec_cfg *cfg) +{ + cfg->promiscuous_mode_enable = false; + cfg->pause_ignore = false; + cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH; + cfg->max_frame_length = DEFAULT_MAX_FRAME_LENGTH; + cfg->pause_quant = DEFAULT_PAUSE_QUANT; +} + +static int init(struct tgec_regs __iomem *regs, struct tgec_cfg *cfg, + u32 exception_mask) +{ + u32 tmp; + + /* Config */ + tmp = CMF_CFG_CRC_FWD; + if (cfg->promiscuous_mode_enable) + tmp |= CMD_CFG_PROMIS_EN; + if (cfg->pause_ignore) + tmp |= CMD_CFG_PAUSE_IGNORE; + /* Payload length check disable */ + tmp |= CMD_CFG_NO_LEN_CHK; + iowrite32be(tmp, ®s->command_config); + + /* Max Frame Length */ + iowrite32be((u32)cfg->max_frame_length, ®s->maxfrm); + /* Pause Time */ + iowrite32be(cfg->pause_quant, ®s->pause_quant); + + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + iowrite32be(ioread32be(®s->imask) | exception_mask, ®s->imask); + + return 0; +} + +static int check_init_parameters(struct fman_mac *tgec) +{ + if (tgec->max_speed < SPEED_10000) { + pr_err("10G MAC driver only support 10G speed\n"); + return -EINVAL; + } + if (!tgec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!tgec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_MDIO_SCAN_EVENT: + bit_mask = TGEC_IMASK_MDIO_SCAN_EVENT; + break; + case FM_MAC_EX_10G_MDIO_CMD_CMPL: + bit_mask = TGEC_IMASK_MDIO_CMD_CMPL; + break; + case FM_MAC_EX_10G_REM_FAULT: + bit_mask = TGEC_IMASK_REM_FAULT; + break; + case FM_MAC_EX_10G_LOC_FAULT: + bit_mask = TGEC_IMASK_LOC_FAULT; + break; + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = TGEC_IMASK_TX_ECC_ER; + break; + case FM_MAC_EX_10G_TX_FIFO_UNFL: + bit_mask = TGEC_IMASK_TX_FIFO_UNFL; + break; + case FM_MAC_EX_10G_TX_FIFO_OVFL: + bit_mask = TGEC_IMASK_TX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_TX_ER: + bit_mask = TGEC_IMASK_TX_ER; + break; + case FM_MAC_EX_10G_RX_FIFO_OVFL: + bit_mask = TGEC_IMASK_RX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = TGEC_IMASK_RX_ECC_ER; + break; + case FM_MAC_EX_10G_RX_JAB_FRM: + bit_mask = TGEC_IMASK_RX_JAB_FRM; + break; + case FM_MAC_EX_10G_RX_OVRSZ_FRM: + bit_mask = TGEC_IMASK_RX_OVRSZ_FRM; + break; + case FM_MAC_EX_10G_RX_RUNT_FRM: + bit_mask = TGEC_IMASK_RX_RUNT_FRM; + break; + case FM_MAC_EX_10G_RX_FRAG_FRM: + bit_mask = TGEC_IMASK_RX_FRAG_FRM; + break; + case FM_MAC_EX_10G_RX_LEN_ER: + bit_mask = TGEC_IMASK_RX_LEN_ER; + break; + case FM_MAC_EX_10G_RX_CRC_ER: + bit_mask = TGEC_IMASK_RX_CRC_ER; + break; + case FM_MAC_EX_10G_RX_ALIGN_ER: + bit_mask = TGEC_IMASK_RX_ALIGN_ER; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void tgec_err_exception(void *handle) +{ + struct fman_mac *tgec = (struct fman_mac *)handle; + struct tgec_regs __iomem *regs = tgec->regs; + u32 event; + + /* do not handle MDIO events */ + event = ioread32be(®s->ievent) & + ~(TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_MDIO_CMD_CMPL); + + event &= ioread32be(®s->imask); + + iowrite32be(event, ®s->ievent); + + if (event & TGEC_IMASK_REM_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_REM_FAULT); + if (event & TGEC_IMASK_LOC_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_LOC_FAULT); + if (event & TGEC_IMASK_TX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & TGEC_IMASK_TX_FIFO_UNFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_UNFL); + if (event & TGEC_IMASK_TX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_OVFL); + if (event & TGEC_IMASK_TX_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ER); + if (event & TGEC_IMASK_RX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FIFO_OVFL); + if (event & TGEC_IMASK_RX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ECC_ER); + if (event & TGEC_IMASK_RX_JAB_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_JAB_FRM); + if (event & TGEC_IMASK_RX_OVRSZ_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_OVRSZ_FRM); + if (event & TGEC_IMASK_RX_RUNT_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_RUNT_FRM); + if (event & TGEC_IMASK_RX_FRAG_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FRAG_FRM); + if (event & TGEC_IMASK_RX_LEN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_LEN_ER); + if (event & TGEC_IMASK_RX_CRC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_CRC_ER); + if (event & TGEC_IMASK_RX_ALIGN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ALIGN_ER); +} + +static void free_init_resources(struct fman_mac *tgec) +{ + fman_unregister_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id, + FMAN_INTR_TYPE_ERR); + + /* release the driver's group hash table */ + free_hash_table(tgec->multicast_addr_hash); + tgec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(tgec->unicast_addr_hash); + tgec->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct tgec_cfg *cfg) +{ + /* Checks if tGEC driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +int tgec_enable(struct fman_mac *tgec, enum comm_mode mode) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp |= CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_disable(struct fman_mac *tgec, enum comm_mode mode) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp &= ~CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; + else + tmp &= ~CMD_CFG_PROMIS_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val) +{ + if (is_init_done(tgec->cfg)) + return -EINVAL; + + tgec->cfg->max_frame_length = new_val; + + return 0; +} + +int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + iowrite32be((u32)pause_time, ®s->pause_quant); + + return 0; +} + +int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (!en) + tmp |= CMD_CFG_PAUSE_IGNORE; + else + tmp &= ~CMD_CFG_PAUSE_IGNORE; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *p_enet_addr) +{ + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr); + set_mac_address(tgec->regs, (u8 *)(*p_enet_addr)); + + return 0; +} + +int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +{ + struct tgec_regs __iomem *regs = tgec->regs; + struct eth_hash_entry *hash_entry; + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + /* CRC calculation */ + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_ATOMIC); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &tgec->multicast_addr_hash->lsts[hash]); + iowrite32be((hash | TGEC_HASH_MCAST_EN), ®s->hashtable_ctrl); + + return 0; +} + +int tgec_set_allmulti(struct fman_mac *tgec, bool enable) +{ + u32 entry; + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + if (enable) { + for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++) + iowrite32be(entry | TGEC_HASH_MCAST_EN, + ®s->hashtable_ctrl); + } else { + for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++) + iowrite32be(entry & ~TGEC_HASH_MCAST_EN, + ®s->hashtable_ctrl); + } + + tgec->allmulti_enabled = enable; + + return 0; +} + +int tgec_set_tstamp(struct fman_mac *tgec, bool enable) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + + if (enable) + tmp |= CMD_CFG_EN_TIMESTAMP; + else + tmp &= ~CMD_CFG_EN_TIMESTAMP; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +{ + struct tgec_regs __iomem *regs = tgec->regs; + struct eth_hash_entry *hash_entry = NULL; + struct list_head *pos; + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + addr = ((*(u64 *)eth_addr) >> 16); + + /* CRC calculation */ + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + list_for_each(pos, &tgec->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry && hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + + if (!tgec->allmulti_enabled) { + if (list_empty(&tgec->multicast_addr_hash->lsts[hash])) + iowrite32be((hash & ~TGEC_HASH_MCAST_EN), + ®s->hashtable_ctrl); + } + + return 0; +} + +int tgec_get_version(struct fman_mac *tgec, u32 *mac_version) +{ + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + *mac_version = ioread32be(®s->tgec_id); + + return 0; +} + +int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 bit_mask = 0; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + tgec->exceptions |= bit_mask; + else + tgec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + iowrite32be(ioread32be(®s->imask) | bit_mask, ®s->imask); + else + iowrite32be(ioread32be(®s->imask) & ~bit_mask, ®s->imask); + + return 0; +} + +int tgec_init(struct fman_mac *tgec) +{ + struct tgec_cfg *cfg; + enet_addr_t eth_addr; + int err; + + if (is_init_done(tgec->cfg)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(tgec); + if (err) + return err; + + cfg = tgec->cfg; + + if (tgec->addr) { + MAKE_ENET_ADDR_FROM_UINT64(tgec->addr, eth_addr); + set_mac_address(tgec->regs, (u8 *)eth_addr); + } + + /* interrupts */ + /* FM_10G_REM_N_LCL_FLT_EX_10GMAC_ERRATA_SW005 Errata workaround */ + if (tgec->fm_rev_info.major <= 2) + tgec->exceptions &= ~(TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT); + + err = init(tgec->regs, cfg, tgec->exceptions); + if (err) { + free_init_resources(tgec); + pr_err("TGEC version doesn't support this i/f mode\n"); + return err; + } + + /* Max Frame Length */ + err = fman_set_mac_max_frame(tgec->fm, tgec->mac_id, + cfg->max_frame_length); + if (err) { + pr_err("Setting max frame length FAILED\n"); + free_init_resources(tgec); + return -EINVAL; + } + + /* FM_TX_FIFO_CORRUPTION_ERRATA_10GMAC_A007 Errata workaround */ + if (tgec->fm_rev_info.major == 2) { + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + /* restore the default tx ipg Length */ + tmp = (ioread32be(®s->tx_ipg_len) & + ~TGEC_TX_IPG_LENGTH_MASK) | 12; + + iowrite32be(tmp, ®s->tx_ipg_len); + } + + tgec->multicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->multicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + tgec->unicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->unicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fman_register_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id, + FMAN_INTR_TYPE_ERR, tgec_err_exception, tgec); + + kfree(cfg); + tgec->cfg = NULL; + + return 0; +} + +int tgec_free(struct fman_mac *tgec) +{ + free_init_resources(tgec); + + kfree(tgec->cfg); + kfree(tgec); + + return 0; +} + +struct fman_mac *tgec_config(struct fman_mac_params *params) +{ + struct fman_mac *tgec; + struct tgec_cfg *cfg; + void __iomem *base_addr; + + base_addr = params->base_addr; + /* allocate memory for the UCC GETH data structure. */ + tgec = kzalloc(sizeof(*tgec), GFP_KERNEL); + if (!tgec) + return NULL; + + /* allocate memory for the 10G MAC driver parameters data structure. */ + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + tgec_free(tgec); + return NULL; + } + + /* Plant parameter structure pointer */ + tgec->cfg = cfg; + + set_dflts(cfg); + + tgec->regs = base_addr; + tgec->addr = ENET_ADDR_TO_UINT64(params->addr); + tgec->max_speed = params->max_speed; + tgec->mac_id = params->mac_id; + tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT | + TGEC_IMASK_TX_ECC_ER | + TGEC_IMASK_TX_FIFO_UNFL | + TGEC_IMASK_TX_FIFO_OVFL | + TGEC_IMASK_TX_ER | + TGEC_IMASK_RX_FIFO_OVFL | + TGEC_IMASK_RX_ECC_ER | + TGEC_IMASK_RX_JAB_FRM | + TGEC_IMASK_RX_OVRSZ_FRM | + TGEC_IMASK_RX_RUNT_FRM | + TGEC_IMASK_RX_FRAG_FRM | + TGEC_IMASK_RX_CRC_ER | + TGEC_IMASK_RX_ALIGN_ER); + tgec->exception_cb = params->exception_cb; + tgec->event_cb = params->event_cb; + tgec->dev_id = params->dev_id; + tgec->fm = params->fm; + + /* Save FMan revision */ + fman_get_revision(tgec->fm, &tgec->fm_rev_info); + + return tgec; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h new file mode 100644 index 000000000..3bfd1062b --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h @@ -0,0 +1,57 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __TGEC_H +#define __TGEC_H + +#include "fman_mac.h" + +struct fman_mac *tgec_config(struct fman_mac_params *params); +int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val); +int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *enet_addr); +int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val); +int tgec_enable(struct fman_mac *tgec, enum comm_mode mode); +int tgec_disable(struct fman_mac *tgec, enum comm_mode mode); +int tgec_init(struct fman_mac *tgec); +int tgec_free(struct fman_mac *tgec); +int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en); +int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 priority, + u16 pause_time, u16 thresh_time); +int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable); +int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); +int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); +int tgec_get_version(struct fman_mac *tgec, u32 *mac_version); +int tgec_set_allmulti(struct fman_mac *tgec, bool enable); +int tgec_set_tstamp(struct fman_mac *tgec, bool enable); + +#endif /* __TGEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c new file mode 100644 index 000000000..3312dc408 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -0,0 +1,904 @@ +/* Copyright 2008-2015 Freescale Semiconductor, Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_net.h> +#include <linux/of_mdio.h> +#include <linux/device.h> +#include <linux/phy.h> +#include <linux/netdevice.h> +#include <linux/phy_fixed.h> +#include <linux/etherdevice.h> +#include <linux/libfdt_env.h> + +#include "mac.h" +#include "fman_mac.h" +#include "fman_dtsec.h" +#include "fman_tgec.h" +#include "fman_memac.h" + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("FSL FMan MAC API based driver"); + +struct mac_priv_s { + struct device *dev; + void __iomem *vaddr; + u8 cell_index; + struct fman *fman; + struct device_node *internal_phy_node; + /* List of multicast addresses */ + struct list_head mc_addr_list; + struct platform_device *eth_dev; + struct fixed_phy_status *fixed_link; + u16 speed; + u16 max_speed; + + int (*enable)(struct fman_mac *mac_dev, enum comm_mode mode); + int (*disable)(struct fman_mac *mac_dev, enum comm_mode mode); +}; + +struct mac_address { + u8 addr[ETH_ALEN]; + struct list_head list; +}; + +static void mac_exception(void *handle, enum fman_mac_exceptions ex) +{ + struct mac_device *mac_dev; + struct mac_priv_s *priv; + + mac_dev = handle; + priv = mac_dev->priv; + + if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) { + /* don't flag RX FIFO after the first */ + mac_dev->set_exception(mac_dev->fman_mac, + FM_MAC_EX_10G_RX_FIFO_OVFL, false); + dev_err(priv->dev, "10G MAC got RX FIFO Error = %x\n", ex); + } + + dev_dbg(priv->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c", + __func__, ex); +} + +static int set_fman_mac_params(struct mac_device *mac_dev, + struct fman_mac_params *params) +{ + struct mac_priv_s *priv = mac_dev->priv; + + params->base_addr = (typeof(params->base_addr)) + devm_ioremap(priv->dev, mac_dev->res->start, + resource_size(mac_dev->res)); + if (!params->base_addr) + return -ENOMEM; + + memcpy(¶ms->addr, mac_dev->addr, sizeof(mac_dev->addr)); + params->max_speed = priv->max_speed; + params->phy_if = mac_dev->phy_if; + params->basex_if = false; + params->mac_id = priv->cell_index; + params->fm = (void *)priv->fman; + params->exception_cb = mac_exception; + params->event_cb = mac_exception; + params->dev_id = mac_dev; + params->internal_phy_node = priv->internal_phy_node; + + return 0; +} + +static int tgec_initialization(struct mac_device *mac_dev) +{ + int err; + struct mac_priv_s *priv; + struct fman_mac_params params; + u32 version; + + priv = mac_dev->priv; + + err = set_fman_mac_params(mac_dev, ¶ms); + if (err) + goto _return; + + mac_dev->fman_mac = tgec_config(¶ms); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + err = tgec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); + if (err < 0) + goto _return_fm_mac_free; + + err = tgec_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fm_mac_free; + + /* For 10G MAC, disable Tx ECC exception */ + err = mac_dev->set_exception(mac_dev->fman_mac, + FM_MAC_EX_10G_TX_ECC_ER, false); + if (err < 0) + goto _return_fm_mac_free; + + err = tgec_get_version(mac_dev->fman_mac, &version); + if (err < 0) + goto _return_fm_mac_free; + + dev_info(priv->dev, "FMan XGEC version: 0x%08x\n", version); + + goto _return; + +_return_fm_mac_free: + tgec_free(mac_dev->fman_mac); + +_return: + return err; +} + +static int dtsec_initialization(struct mac_device *mac_dev) +{ + int err; + struct mac_priv_s *priv; + struct fman_mac_params params; + u32 version; + + priv = mac_dev->priv; + + err = set_fman_mac_params(mac_dev, ¶ms); + if (err) + goto _return; + + mac_dev->fman_mac = dtsec_config(¶ms); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + err = dtsec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); + if (err < 0) + goto _return_fm_mac_free; + + err = dtsec_cfg_pad_and_crc(mac_dev->fman_mac, true); + if (err < 0) + goto _return_fm_mac_free; + + err = dtsec_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fm_mac_free; + + /* For 1G MAC, disable by default the MIB counters overflow interrupt */ + err = mac_dev->set_exception(mac_dev->fman_mac, + FM_MAC_EX_1G_RX_MIB_CNT_OVFL, false); + if (err < 0) + goto _return_fm_mac_free; + + err = dtsec_get_version(mac_dev->fman_mac, &version); + if (err < 0) + goto _return_fm_mac_free; + + dev_info(priv->dev, "FMan dTSEC version: 0x%08x\n", version); + + goto _return; + +_return_fm_mac_free: + dtsec_free(mac_dev->fman_mac); + +_return: + return err; +} + +static int memac_initialization(struct mac_device *mac_dev) +{ + int err; + struct mac_priv_s *priv; + struct fman_mac_params params; + + priv = mac_dev->priv; + + err = set_fman_mac_params(mac_dev, ¶ms); + if (err) + goto _return; + + if (priv->max_speed == SPEED_10000) + params.phy_if = PHY_INTERFACE_MODE_XGMII; + + mac_dev->fman_mac = memac_config(¶ms); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + err = memac_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); + if (err < 0) + goto _return_fm_mac_free; + + err = memac_cfg_reset_on_init(mac_dev->fman_mac, true); + if (err < 0) + goto _return_fm_mac_free; + + err = memac_cfg_fixed_link(mac_dev->fman_mac, priv->fixed_link); + if (err < 0) + goto _return_fm_mac_free; + + err = memac_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fm_mac_free; + + dev_info(priv->dev, "FMan MEMAC\n"); + + goto _return; + +_return_fm_mac_free: + memac_free(mac_dev->fman_mac); + +_return: + return err; +} + +static int start(struct mac_device *mac_dev) +{ + int err; + struct phy_device *phy_dev = mac_dev->phy_dev; + struct mac_priv_s *priv = mac_dev->priv; + + err = priv->enable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX); + if (!err && phy_dev) + phy_start(phy_dev); + + return err; +} + +static int stop(struct mac_device *mac_dev) +{ + struct mac_priv_s *priv = mac_dev->priv; + + if (mac_dev->phy_dev) + phy_stop(mac_dev->phy_dev); + + return priv->disable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX); +} + +static int set_multi(struct net_device *net_dev, struct mac_device *mac_dev) +{ + struct mac_priv_s *priv; + struct mac_address *old_addr, *tmp; + struct netdev_hw_addr *ha; + int err; + enet_addr_t *addr; + + priv = mac_dev->priv; + + /* Clear previous address list */ + list_for_each_entry_safe(old_addr, tmp, &priv->mc_addr_list, list) { + addr = (enet_addr_t *)old_addr->addr; + err = mac_dev->remove_hash_mac_addr(mac_dev->fman_mac, addr); + if (err < 0) + return err; + + list_del(&old_addr->list); + kfree(old_addr); + } + + /* Add all the addresses from the new list */ + netdev_for_each_mc_addr(ha, net_dev) { + addr = (enet_addr_t *)ha->addr; + err = mac_dev->add_hash_mac_addr(mac_dev->fman_mac, addr); + if (err < 0) + return err; + + tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC); + if (!tmp) + return -ENOMEM; + + ether_addr_copy(tmp->addr, ha->addr); + list_add(&tmp->list, &priv->mc_addr_list); + } + return 0; +} + +/** + * fman_set_mac_active_pause + * @mac_dev: A pointer to the MAC device + * @rx: Pause frame setting for RX + * @tx: Pause frame setting for TX + * + * Set the MAC RX/TX PAUSE frames settings + * + * Avoid redundant calls to FMD, if the MAC driver already contains the desired + * active PAUSE settings. Otherwise, the new active settings should be reflected + * in FMan. + * + * Return: 0 on success; Error code otherwise. + */ +int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx) +{ + struct fman_mac *fman_mac = mac_dev->fman_mac; + int err = 0; + + if (rx != mac_dev->rx_pause_active) { + err = mac_dev->set_rx_pause(fman_mac, rx); + if (likely(err == 0)) + mac_dev->rx_pause_active = rx; + } + + if (tx != mac_dev->tx_pause_active) { + u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE : + FSL_FM_PAUSE_TIME_DISABLE); + + err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0); + + if (likely(err == 0)) + mac_dev->tx_pause_active = tx; + } + + return err; +} +EXPORT_SYMBOL(fman_set_mac_active_pause); + +/** + * fman_get_pause_cfg + * @mac_dev: A pointer to the MAC device + * @rx_pause: Return value for RX setting + * @tx_pause: Return value for TX setting + * + * Determine the MAC RX/TX PAUSE frames settings based on PHY + * autonegotiation or values set by eththool. + * + * Return: Pointer to FMan device. + */ +void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, + bool *tx_pause) +{ + struct phy_device *phy_dev = mac_dev->phy_dev; + u16 lcl_adv, rmt_adv; + u8 flowctrl; + + *rx_pause = *tx_pause = false; + + if (!phy_dev->duplex) + return; + + /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings + * are those set by ethtool. + */ + if (!mac_dev->autoneg_pause) { + *rx_pause = mac_dev->rx_pause_req; + *tx_pause = mac_dev->tx_pause_req; + return; + } + + /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE + * settings depend on the result of the link negotiation. + */ + + /* get local capabilities */ + lcl_adv = linkmode_adv_to_lcl_adv_t(phy_dev->advertising); + + /* get link partner capabilities */ + rmt_adv = 0; + if (phy_dev->pause) + rmt_adv |= LPA_PAUSE_CAP; + if (phy_dev->asym_pause) + rmt_adv |= LPA_PAUSE_ASYM; + + /* Calculate TX/RX settings based on local and peer advertised + * symmetric/asymmetric PAUSE capabilities. + */ + flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (flowctrl & FLOW_CTRL_RX) + *rx_pause = true; + if (flowctrl & FLOW_CTRL_TX) + *tx_pause = true; +} +EXPORT_SYMBOL(fman_get_pause_cfg); + +static void adjust_link_void(struct mac_device *mac_dev) +{ +} + +static void adjust_link_dtsec(struct mac_device *mac_dev) +{ + struct phy_device *phy_dev = mac_dev->phy_dev; + struct fman_mac *fman_mac; + bool rx_pause, tx_pause; + int err; + + fman_mac = mac_dev->fman_mac; + if (!phy_dev->link) { + dtsec_restart_autoneg(fman_mac); + + return; + } + + dtsec_adjust_link(fman_mac, phy_dev->speed); + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + dev_err(mac_dev->priv->dev, "fman_set_mac_active_pause() = %d\n", + err); +} + +static void adjust_link_memac(struct mac_device *mac_dev) +{ + struct phy_device *phy_dev = mac_dev->phy_dev; + struct fman_mac *fman_mac; + bool rx_pause, tx_pause; + int err; + + fman_mac = mac_dev->fman_mac; + memac_adjust_link(fman_mac, phy_dev->speed); + + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + dev_err(mac_dev->priv->dev, "fman_set_mac_active_pause() = %d\n", + err); +} + +static void setup_dtsec(struct mac_device *mac_dev) +{ + mac_dev->init = dtsec_initialization; + mac_dev->set_promisc = dtsec_set_promiscuous; + mac_dev->change_addr = dtsec_modify_mac_address; + mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address; + mac_dev->set_tx_pause = dtsec_set_tx_pause_frames; + mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames; + mac_dev->set_exception = dtsec_set_exception; + mac_dev->set_allmulti = dtsec_set_allmulti; + mac_dev->set_tstamp = dtsec_set_tstamp; + mac_dev->set_multi = set_multi; + mac_dev->start = start; + mac_dev->stop = stop; + mac_dev->adjust_link = adjust_link_dtsec; + mac_dev->priv->enable = dtsec_enable; + mac_dev->priv->disable = dtsec_disable; +} + +static void setup_tgec(struct mac_device *mac_dev) +{ + mac_dev->init = tgec_initialization; + mac_dev->set_promisc = tgec_set_promiscuous; + mac_dev->change_addr = tgec_modify_mac_address; + mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address; + mac_dev->set_tx_pause = tgec_set_tx_pause_frames; + mac_dev->set_rx_pause = tgec_accept_rx_pause_frames; + mac_dev->set_exception = tgec_set_exception; + mac_dev->set_allmulti = tgec_set_allmulti; + mac_dev->set_tstamp = tgec_set_tstamp; + mac_dev->set_multi = set_multi; + mac_dev->start = start; + mac_dev->stop = stop; + mac_dev->adjust_link = adjust_link_void; + mac_dev->priv->enable = tgec_enable; + mac_dev->priv->disable = tgec_disable; +} + +static void setup_memac(struct mac_device *mac_dev) +{ + mac_dev->init = memac_initialization; + mac_dev->set_promisc = memac_set_promiscuous; + mac_dev->change_addr = memac_modify_mac_address; + mac_dev->add_hash_mac_addr = memac_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address; + mac_dev->set_tx_pause = memac_set_tx_pause_frames; + mac_dev->set_rx_pause = memac_accept_rx_pause_frames; + mac_dev->set_exception = memac_set_exception; + mac_dev->set_allmulti = memac_set_allmulti; + mac_dev->set_tstamp = memac_set_tstamp; + mac_dev->set_multi = set_multi; + mac_dev->start = start; + mac_dev->stop = stop; + mac_dev->adjust_link = adjust_link_memac; + mac_dev->priv->enable = memac_enable; + mac_dev->priv->disable = memac_disable; +} + +#define DTSEC_SUPPORTED \ + (SUPPORTED_10baseT_Half \ + | SUPPORTED_10baseT_Full \ + | SUPPORTED_100baseT_Half \ + | SUPPORTED_100baseT_Full \ + | SUPPORTED_Autoneg \ + | SUPPORTED_Pause \ + | SUPPORTED_Asym_Pause \ + | SUPPORTED_MII) + +static DEFINE_MUTEX(eth_lock); + +static const u16 phy2speed[] = { + [PHY_INTERFACE_MODE_MII] = SPEED_100, + [PHY_INTERFACE_MODE_GMII] = SPEED_1000, + [PHY_INTERFACE_MODE_SGMII] = SPEED_1000, + [PHY_INTERFACE_MODE_TBI] = SPEED_1000, + [PHY_INTERFACE_MODE_RMII] = SPEED_100, + [PHY_INTERFACE_MODE_RGMII] = SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000, + [PHY_INTERFACE_MODE_RTBI] = SPEED_1000, + [PHY_INTERFACE_MODE_QSGMII] = SPEED_1000, + [PHY_INTERFACE_MODE_XGMII] = SPEED_10000 +}; + +static struct platform_device *dpaa_eth_add_device(int fman_id, + struct mac_device *mac_dev) +{ + struct platform_device *pdev; + struct dpaa_eth_data data; + struct mac_priv_s *priv; + static int dpaa_eth_dev_cnt; + int ret; + + priv = mac_dev->priv; + + data.mac_dev = mac_dev; + data.mac_hw_id = priv->cell_index; + data.fman_hw_id = fman_id; + + mutex_lock(ð_lock); + pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt); + if (!pdev) { + ret = -ENOMEM; + goto no_mem; + } + + pdev->dev.parent = priv->dev; + + ret = platform_device_add_data(pdev, &data, sizeof(data)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + + dpaa_eth_dev_cnt++; + mutex_unlock(ð_lock); + + return pdev; + +err: + platform_device_put(pdev); +no_mem: + mutex_unlock(ð_lock); + + return ERR_PTR(ret); +} + +static const struct of_device_id mac_match[] = { + { .compatible = "fsl,fman-dtsec" }, + { .compatible = "fsl,fman-xgec" }, + { .compatible = "fsl,fman-memac" }, + {} +}; +MODULE_DEVICE_TABLE(of, mac_match); + +static int mac_probe(struct platform_device *_of_dev) +{ + int err, i, nph; + struct device *dev; + struct device_node *mac_node, *dev_node; + struct mac_device *mac_dev; + struct platform_device *of_dev; + struct resource res; + struct mac_priv_s *priv; + const u8 *mac_addr; + u32 val; + u8 fman_id; + phy_interface_t phy_if; + + dev = &_of_dev->dev; + mac_node = dev->of_node; + + mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL); + if (!mac_dev) { + err = -ENOMEM; + goto _return; + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + goto _return; + } + + /* Save private information */ + mac_dev->priv = priv; + priv->dev = dev; + + if (of_device_is_compatible(mac_node, "fsl,fman-dtsec")) { + setup_dtsec(mac_dev); + priv->internal_phy_node = of_parse_phandle(mac_node, + "tbi-handle", 0); + } else if (of_device_is_compatible(mac_node, "fsl,fman-xgec")) { + setup_tgec(mac_dev); + } else if (of_device_is_compatible(mac_node, "fsl,fman-memac")) { + setup_memac(mac_dev); + priv->internal_phy_node = of_parse_phandle(mac_node, + "pcsphy-handle", 0); + } else { + dev_err(dev, "MAC node (%pOF) contains unsupported MAC\n", + mac_node); + err = -EINVAL; + goto _return; + } + + INIT_LIST_HEAD(&priv->mc_addr_list); + + /* Get the FM node */ + dev_node = of_get_parent(mac_node); + if (!dev_node) { + dev_err(dev, "of_get_parent(%pOF) failed\n", + mac_node); + err = -EINVAL; + goto _return_of_get_parent; + } + + of_dev = of_find_device_by_node(dev_node); + if (!of_dev) { + dev_err(dev, "of_find_device_by_node(%pOF) failed\n", dev_node); + err = -EINVAL; + goto _return_of_node_put; + } + + /* Get the FMan cell-index */ + err = of_property_read_u32(dev_node, "cell-index", &val); + if (err) { + dev_err(dev, "failed to read cell-index for %pOF\n", dev_node); + err = -EINVAL; + goto _return_of_node_put; + } + /* cell-index 0 => FMan id 1 */ + fman_id = (u8)(val + 1); + + priv->fman = fman_bind(&of_dev->dev); + if (!priv->fman) { + dev_err(dev, "fman_bind(%pOF) failed\n", dev_node); + err = -ENODEV; + goto _return_of_node_put; + } + + of_node_put(dev_node); + + /* Get the address of the memory mapped registers */ + err = of_address_to_resource(mac_node, 0, &res); + if (err < 0) { + dev_err(dev, "of_address_to_resource(%pOF) = %d\n", + mac_node, err); + goto _return_of_get_parent; + } + + mac_dev->res = __devm_request_region(dev, + fman_get_mem_region(priv->fman), + res.start, resource_size(&res), + "mac"); + if (!mac_dev->res) { + dev_err(dev, "__devm_request_mem_region(mac) failed\n"); + err = -EBUSY; + goto _return_of_get_parent; + } + + priv->vaddr = devm_ioremap(dev, mac_dev->res->start, + resource_size(mac_dev->res)); + if (!priv->vaddr) { + dev_err(dev, "devm_ioremap() failed\n"); + err = -EIO; + goto _return_of_get_parent; + } + + if (!of_device_is_available(mac_node)) { + err = -ENODEV; + goto _return_of_get_parent; + } + + /* Get the cell-index */ + err = of_property_read_u32(mac_node, "cell-index", &val); + if (err) { + dev_err(dev, "failed to read cell-index for %pOF\n", mac_node); + err = -EINVAL; + goto _return_of_get_parent; + } + priv->cell_index = (u8)val; + + /* Get the MAC address */ + mac_addr = of_get_mac_address(mac_node); + if (IS_ERR(mac_addr)) + dev_warn(dev, "of_get_mac_address(%pOF) failed\n", mac_node); + else + ether_addr_copy(mac_dev->addr, mac_addr); + + /* Get the port handles */ + nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL); + if (unlikely(nph < 0)) { + dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n", + mac_node); + err = nph; + goto _return_of_get_parent; + } + + if (nph != ARRAY_SIZE(mac_dev->port)) { + dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n", + mac_node); + err = -EINVAL; + goto _return_of_get_parent; + } + + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { + /* Find the port node */ + dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i); + if (!dev_node) { + dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n", + mac_node); + err = -EINVAL; + goto _return_of_node_put; + } + + of_dev = of_find_device_by_node(dev_node); + if (!of_dev) { + dev_err(dev, "of_find_device_by_node(%pOF) failed\n", + dev_node); + err = -EINVAL; + goto _return_of_node_put; + } + + mac_dev->port[i] = fman_port_bind(&of_dev->dev); + if (!mac_dev->port[i]) { + dev_err(dev, "dev_get_drvdata(%pOF) failed\n", + dev_node); + err = -EINVAL; + goto _return_of_node_put; + } + of_node_put(dev_node); + } + + /* Get the PHY connection type */ + err = of_get_phy_mode(mac_node, &phy_if); + if (err) { + dev_warn(dev, + "of_get_phy_mode() for %pOF failed. Defaulting to SGMII\n", + mac_node); + phy_if = PHY_INTERFACE_MODE_SGMII; + } + mac_dev->phy_if = phy_if; + + priv->speed = phy2speed[mac_dev->phy_if]; + priv->max_speed = priv->speed; + mac_dev->if_support = DTSEC_SUPPORTED; + /* We don't support half-duplex in SGMII mode */ + if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) + mac_dev->if_support &= ~(SUPPORTED_10baseT_Half | + SUPPORTED_100baseT_Half); + + /* Gigabit support (no half-duplex) */ + if (priv->max_speed == 1000) + mac_dev->if_support |= SUPPORTED_1000baseT_Full; + + /* The 10G interface only supports one mode */ + if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) + mac_dev->if_support = SUPPORTED_10000baseT_Full; + + /* Get the rest of the PHY information */ + mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); + if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { + struct phy_device *phy; + + err = of_phy_register_fixed_link(mac_node); + if (err) + goto _return_of_get_parent; + + priv->fixed_link = kzalloc(sizeof(*priv->fixed_link), + GFP_KERNEL); + if (!priv->fixed_link) { + err = -ENOMEM; + goto _return_of_get_parent; + } + + mac_dev->phy_node = of_node_get(mac_node); + phy = of_phy_find_device(mac_dev->phy_node); + if (!phy) { + err = -EINVAL; + of_node_put(mac_dev->phy_node); + goto _return_of_get_parent; + } + + priv->fixed_link->link = phy->link; + priv->fixed_link->speed = phy->speed; + priv->fixed_link->duplex = phy->duplex; + priv->fixed_link->pause = phy->pause; + priv->fixed_link->asym_pause = phy->asym_pause; + + put_device(&phy->mdio.dev); + } + + err = mac_dev->init(mac_dev); + if (err < 0) { + dev_err(dev, "mac_dev->init() = %d\n", err); + of_node_put(mac_dev->phy_node); + goto _return_of_get_parent; + } + + /* pause frame autonegotiation enabled */ + mac_dev->autoneg_pause = true; + + /* By intializing the values to false, force FMD to enable PAUSE frames + * on RX and TX + */ + mac_dev->rx_pause_req = true; + mac_dev->tx_pause_req = true; + mac_dev->rx_pause_active = false; + mac_dev->tx_pause_active = false; + err = fman_set_mac_active_pause(mac_dev, true, true); + if (err < 0) + dev_err(dev, "fman_set_mac_active_pause() = %d\n", err); + + if (!IS_ERR(mac_addr)) + dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr); + + priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev); + if (IS_ERR(priv->eth_dev)) { + dev_err(dev, "failed to add Ethernet platform device for MAC %d\n", + priv->cell_index); + priv->eth_dev = NULL; + } + + goto _return; + +_return_of_node_put: + of_node_put(dev_node); +_return_of_get_parent: + kfree(priv->fixed_link); +_return: + return err; +} + +static int mac_remove(struct platform_device *pdev) +{ + struct mac_device *mac_dev = platform_get_drvdata(pdev); + + platform_device_unregister(mac_dev->priv->eth_dev); + return 0; +} + +static struct platform_driver mac_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = mac_match, + }, + .probe = mac_probe, + .remove = mac_remove, +}; + +builtin_platform_driver(mac_driver); diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h new file mode 100644 index 000000000..824a81a9f --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac.h @@ -0,0 +1,101 @@ +/* Copyright 2008-2015 Freescale Semiconductor, Inc. + * + * 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 Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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 __MAC_H +#define __MAC_H + +#include <linux/device.h> +#include <linux/if_ether.h> +#include <linux/phy.h> +#include <linux/list.h> + +#include "fman_port.h" +#include "fman.h" +#include "fman_mac.h" + +struct fman_mac; +struct mac_priv_s; + +struct mac_device { + struct resource *res; + u8 addr[ETH_ALEN]; + struct fman_port *port[2]; + u32 if_support; + struct phy_device *phy_dev; + phy_interface_t phy_if; + struct device_node *phy_node; + + bool autoneg_pause; + bool rx_pause_req; + bool tx_pause_req; + bool rx_pause_active; + bool tx_pause_active; + bool promisc; + bool allmulti; + + int (*init)(struct mac_device *mac_dev); + int (*start)(struct mac_device *mac_dev); + int (*stop)(struct mac_device *mac_dev); + void (*adjust_link)(struct mac_device *mac_dev); + int (*set_promisc)(struct fman_mac *mac_dev, bool enable); + int (*change_addr)(struct fman_mac *mac_dev, enet_addr_t *enet_addr); + int (*set_allmulti)(struct fman_mac *mac_dev, bool enable); + int (*set_tstamp)(struct fman_mac *mac_dev, bool enable); + int (*set_multi)(struct net_device *net_dev, + struct mac_device *mac_dev); + int (*set_rx_pause)(struct fman_mac *mac_dev, bool en); + int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority, + u16 pause_time, u16 thresh_time); + int (*set_exception)(struct fman_mac *mac_dev, + enum fman_mac_exceptions exception, bool enable); + int (*add_hash_mac_addr)(struct fman_mac *mac_dev, + enet_addr_t *eth_addr); + int (*remove_hash_mac_addr)(struct fman_mac *mac_dev, + enet_addr_t *eth_addr); + + struct fman_mac *fman_mac; + struct mac_priv_s *priv; +}; + +struct dpaa_eth_data { + struct mac_device *mac_dev; + int mac_hw_id; + int fman_hw_id; +}; + +extern const char *mac_driver_description; + +int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx); + +void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, + bool *tx_pause); + +#endif /* __MAC_H */ |