Commit 7b752fd3 authored by David S. Miller's avatar David S. Miller

Merge branch 'fsl-fmain'

Igal Liberman says:

====================
Freescale DPAA FMan

The Freescale Data Path Acceleration Architecture (DPAA) is a set
of hardware components on specific QorIQ multicore processors.
This architecture provides the infrastructure to support
simplified sharing of networking interfaces and accelerators
by multiple CPU cores and the accelerators.

One of the DPAA accelerators is the Frame Manager (FMan)
which contains a series of hardware blocks: ports, Ethernet MACs,
a multi user RAM (MURAM) and Storage Profile (SP).

This patch set introduce the FMan drivers.
Each driver configures and initializes the corresponding
FMan hardware module (described above).
The MAC driver offers support for three different
types of MACs (eTSEC, TGEC, MEMAC).

v9 --> v10:
	- Addressed feedback from David Miller
		Remove private CRC implementation
	- Addressed feedback from Kenneth Klette Jonassen:
		- Use Kernel PHY API to configure dTSEC TBI
		- Use Kernel PHY API to configure mEMAC PCS
		  This patchset requires device tree update:
		  https://patchwork.ozlabs.org/patch/559501/
	- Addressed feedback from Andy Fleming

v8 --> v9:
	No changes

v7 --> v8:
	- Addressed feedback from David Miller
	- Support for ARM:
		- Device tree parsing
		- IO Accessors
		- Addressed compilation issue on non-PPC targets

v6 --> v7:
	- Addressed compilation issue on non-PPC targets
	- Removed B4860 rev 1 support

v5 --> v6:
	- Addressed feedback from Scott:
		- Moved kernel doc to source files
		- Removed a series of configurable settings
		- Miscellaneous code updates

v4 --> v5:
	- Addressed feedback from David Miller:
		- Removed driver layering
		- Reduce namespace pollution
		- Reduce code complexity and size

v3 --> v4:
	- Remove device_initcall call in driver registration (redundant)
	- Remove hot/cold labels
	- Minor update in FMan Clock read from device-tree
	- Update fixed-link support
	- Addressed feedback from Stephen Hemminger
		- Remove bogus blank line

v2 --> v3:
	- Addressed feedback from Scott:
		- Remove typedefs
		- Remove unnecessary memory barriers
		- Remove unnecessary casting
		- Remove KConfig options
		- Remove early_params
		- Remove Hungarian notation
		- Remove __packed__  attribute and padding from structures
		- Remove unlikely attribute (where it's not needed)
		- Use proper error codes and remove unnecessary prints
		- Use proper values for sleep routines
		- Replace complex Macros with functions
		- Improve device tree processing code
		- Use symbolic defines
		- Add time-out in busy-wait loops
		- Removed exit code (loadable module support will be added later)
	- Fixed "fixed-link" issue raised by Joakim Tjernlund

v1 --> v2:
	- Addressed feedback from Paul Bolle:
		- General feedback of FMan Driver layer
		- Remove Errata defines
		- Aligned comments to Kernel Doc
		- Remove Loadable Module support (not yet supported)
		- Removed not needed KConfig dependencies
	- Addressed feedback from Scott Wood
		- Use Kernel ioread/iowrite services
		- Squash FLIB source and header patches together

This submission is based on the prior Freescale DPAA FMan V3,RFC submission.
Several issues addresses in this submission:
	- Reduced MAC layering and complexity
	- Reduced code base
	- T1024/T2080 10G best effort support
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 039f5062 39339616
......@@ -54,6 +54,7 @@ config FEC_MPC52xx_MDIO
If compiled as module, it will be called fec_mpc52xx_phy.
source "drivers/net/ethernet/freescale/fs_enet/Kconfig"
source "drivers/net/ethernet/freescale/fman/Kconfig"
config FSL_PQ_MDIO
tristate "Freescale PQ MDIO"
......
......@@ -17,3 +17,5 @@ gianfar_driver-objs := gianfar.o \
gianfar_ethtool.o
obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
obj-$(CONFIG_FSL_FMAN) += fman/
config FSL_FMAN
bool "FMan support"
depends on FSL_SOC || COMPILE_TEST
select GENERIC_ALLOCATOR
default n
help
Freescale Data-Path Acceleration Architecture Frame Manager
(FMan) support
subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman
obj-y += fsl_fman.o fsl_fman_mac.o fsl_mac.o
fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_port.o
fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o
fsl_mac-objs += mac.o
/*
* 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.h"
#include "fman_muram.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>
/* 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 DMA_OFFSET 0x000C2000
#define FPM_OFFSET 0x000C3000
#define IMEM_OFFSET 0x000C4000
#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
/* 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];
};
/* 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;
};
/* 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 */
};
/** 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);
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;
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;
/* cam section in muram */
int cam_offset;
size_t cam_size;
/* Fifo in MURAM */
int fifo_offset;
size_t fifo_size;
u32 liodn_base[64];
u32 liodn_offset[64];
struct fman_dts_params dts_params;
};
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;
/* 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]);
iowrite32be(liodn_ofst, &fman->bmi_regs->fmbm_spliodn[port_id - 1]);
}
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 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 = 122 * 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;
u64 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->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_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]);
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;
}
/* FMan Reset (supported only for FMan V2) */
if (fman->state->rev_info.major >= 6) {
/* Errata A007273 */
dev_dbg(fman->dev, "%s: FManV3 reset is not supported!\n",
__func__);
} else {
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)
return -EBUSY;
}
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->cam_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);
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
* @mod: Calling module
* @mod_id: Module id (if more than 1 exists, '0' if not)
* @intr_type: Interrupt type (error/normal) selection.
* @f_isr: The interrupt service routine.
* @h_src_arg: Argument to be passed to f_isr.
*
* 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;
}
/**
* fman_unregister_intr
* @fman: A Pointer to FMan device
* @mod: 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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]) ||
(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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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) {
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 {
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;
}
/**
* 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;
}
/* 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.
*/
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.
*
* Could be overridden once, at boot-time, via the
* fm_set_max_frm() callback.
*/
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
* @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)));
}
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;
const u32 *u32_prop;
int lenp, 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);
u32_prop = (const u32 *)of_get_property(fm_node, "cell-index", &lenp);
if (!u32_prop) {
dev_err(&of_dev->dev, "%s: of_get_property(%s, cell-index) failed\n",
__func__, fm_node->full_name);
goto fman_node_put;
}
if (WARN_ON(lenp != sizeof(u32)))
goto fman_node_put;
fman->dts_params.id = (u8)fdt32_to_cpu(u32_prop[0]);
/* 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 resouce\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);
u32_prop = (const u32 *)of_get_property(fm_node,
"fsl,qman-channel-range",
&lenp);
if (!u32_prop) {
dev_err(&of_dev->dev, "%s: of_get_property(%s, fsl,qman-channel-range) failed\n",
__func__, fm_node->full_name);
goto fman_node_put;
}
if (WARN_ON(lenp != sizeof(u32) * 2))
goto fman_node_put;
fman->dts_params.qman_channel_base = fdt32_to_cpu(u32_prop[0]);
fman->dts_params.num_of_qman_channels = fdt32_to_cpu(u32_prop[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_node_put;
}
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_node_put;
}
of_node_put(muram_node);
of_node_put(fm_node);
err = devm_request_irq(&of_dev->dev, irq, fman_irq, 0, "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 == 0) {
dev_err(&of_dev->dev, "%s: devm_ioremap() failed\n", __func__);
goto fman_free;
}
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);
fman->dev = dev;
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, fm_match);
static struct platform_driver fman_driver = {
.driver = {
.name = "fsl-fman",
.of_match_table = fman_match,
},
.probe = fman_probe,
};
builtin_platform_driver(fman_driver);
/*
* 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_H
#define __FM_H
#include <linux/io.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_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 */
u16 l2r; /* Layer 2 result */
u16 l3r; /* Layer 3 result */
u8 l4r; /* Layer 4 result */
u8 cplan; /* Classification plan id */
u16 nxthdr; /* Next Header */
u16 cksum; /* Running-sum */
/* Flags&fragment-offset field of the last IP-header */
u16 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;
};
/* 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);
struct fman *fman_bind(struct device *dev);
#endif /* __FM_H */
/*
* 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 DTSEC_TCTRL_GTS 0x00000020
#define RCTRL_PAL_MASK 0x001f0000
#define RCTRL_PAL_SHIFT 16
#define RCTRL_GHTX 0x00000400
#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_exeption;
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 int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg,
phy_interface_t iface, u16 iface_speed, u8 *macaddr,
u32 exception_mask, u8 tbi_addr)
{
bool is_rgmii, is_sgmii, is_qsgmii;
int i;
u32 tmp;
/* Soft reset */
iowrite32be(MACCFG1_SOFT_RESET, &regs->maccfg1);
iowrite32be(0, &regs->maccfg1);
/* dtsec_id2 */
tmp = ioread32be(&regs->tsec_id2);
/* check RGMII support */
if (iface == PHY_INTERFACE_MODE_RGMII ||
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;
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, &regs->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, &regs->ptv);
tmp = 0;
tmp |= (cfg->rx_prepend << RCTRL_PAL_SHIFT) & RCTRL_PAL_MASK;
/* Accept short frames */
tmp |= RCTRL_RSF;
iowrite32be(tmp, &regs->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, &regs->tbipa);
iowrite32be(0, &regs->tmr_ctrl);
if (cfg->ptp_tsu_en) {
tmp = 0;
tmp |= TMR_PEVENT_TSRE;
iowrite32be(tmp, &regs->tmr_pevent);
if (cfg->ptp_exception_en) {
tmp = 0;
tmp |= TMR_PEMASK_TSREEN;
iowrite32be(tmp, &regs->tmr_pemask);
}
}
tmp = 0;
tmp |= MACCFG1_RX_FLOW;
tmp |= MACCFG1_TX_FLOW;
iowrite32be(tmp, &regs->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, &regs->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, &regs->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, &regs->hafdup);
/* Initialize Maximum frame length */
iowrite32be(cfg->maximum_frame, &regs->maxfrm);
iowrite32be(0xffffffff, &regs->cam1);
iowrite32be(0xffffffff, &regs->cam2);
iowrite32be(exception_mask, &regs->imask);
iowrite32be(0xffffffff, &regs->ievent);
tmp = (u32)((macaddr[5] << 24) |
(macaddr[4] << 16) | (macaddr[3] << 8) | macaddr[2]);
iowrite32be(tmp, &regs->macstnaddr1);
tmp = (u32)((macaddr[1] << 24) | (macaddr[0] << 16));
iowrite32be(tmp, &regs->macstnaddr2);
/* HASH */
for (i = 0; i < NUM_OF_HASH_REGS; i++) {
/* Initialize IADDRx */
iowrite32be(0, &regs->igaddr[i]);
/* Initialize GADDRx */
iowrite32be(0, &regs->gaddr[i]);
}
return 0;
}
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, &regs->macstnaddr1);
tmp = (u32)((adr[1] << 24) | (adr[0] << 16));
iowrite32be(tmp, &regs->macstnaddr2);
}
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 = &regs->gaddr[reg_idx - 8];
else
reg = &regs->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->addr == 0) {
pr_err("Ethernet MAC Must have a valid MAC Address\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(&regs->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(&regs->ievent) &
(u32)(~(DTSEC_IMASK_MMRDEN | DTSEC_IMASK_MMWREN));
event &= ioread32be(&regs->imask);
iowrite32be(event, &regs->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(&regs->tpkt);
/* c. Read the register at dTSEC address offset 0x32C */
tmp_reg1 = ioread32be(&regs->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(&regs->tpkt);
tmp_reg2 = ioread32be(&regs->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(&regs->rctrl) |
RCTRL_GRS, &regs->rctrl);
/* b.Wait until IEVENT[GRSC]=1, or at least
* 100 us has elapsed.
*/
for (i = 0; i < 100; i++) {
if (ioread32be(&regs->ievent) &
DTSEC_IMASK_GRSCEN)
break;
udelay(1);
}
if (ioread32be(&regs->ievent) &
DTSEC_IMASK_GRSCEN)
iowrite32be(DTSEC_IMASK_GRSCEN,
&regs->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(&regs->tmr_pevent);
event &= ioread32be(&regs->tmr_pemask);
if (event) {
iowrite32be(event, &regs->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;
}
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(&regs->maccfg1);
if (mode & COMM_MODE_RX)
tmp |= MACCFG1_RX_EN;
if (mode & COMM_MODE_TX)
tmp |= MACCFG1_TX_EN;
iowrite32be(tmp, &regs->maccfg1);
/* Graceful start - clear the graceful receive stop bit */
if (mode & COMM_MODE_TX)
iowrite32be(ioread32be(&regs->tctrl) & ~DTSEC_TCTRL_GTS,
&regs->tctrl);
if (mode & COMM_MODE_RX)
iowrite32be(ioread32be(&regs->rctrl) & ~RCTRL_GRS,
&regs->rctrl);
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;
/* Gracefull stop - Assert the graceful transmit stop bit */
if (mode & COMM_MODE_RX) {
tmp = ioread32be(&regs->rctrl) | RCTRL_GRS;
iowrite32be(tmp, &regs->rctrl);
if (dtsec->fm_rev_info.major == 2)
usleep_range(100, 200);
else
udelay(10);
}
if (mode & COMM_MODE_TX) {
if (dtsec->fm_rev_info.major == 2)
pr_debug("GTS not supported due to DTSEC_A004 errata.\n");
else
pr_debug("GTS not supported due to DTSEC_A0014 errata.\n");
}
tmp = ioread32be(&regs->maccfg1);
if (mode & COMM_MODE_RX)
tmp &= ~MACCFG1_RX_EN;
if (mode & COMM_MODE_TX)
tmp &= ~MACCFG1_TX_EN;
iowrite32be(tmp, &regs->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;
u32 ptv = 0;
if (!is_init_done(dtsec->dtsec_drv_param))
return -EINVAL;
/* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
if (dtsec->fm_rev_info.major == 2)
if (pause_time < 0 && pause_time <= 320) {
pr_warn("pause-time: %d illegal.Should be > 320\n",
pause_time);
return -EINVAL;
}
if (pause_time) {
ptv = ioread32be(&regs->ptv);
ptv &= PTV_PTE_MASK;
ptv |= pause_time & PTV_PT_MASK;
iowrite32be(ptv, &regs->ptv);
/* trigger the transmission of a flow-control pause frame */
iowrite32be(ioread32be(&regs->maccfg1) | MACCFG1_TX_FLOW,
&regs->maccfg1);
} else
iowrite32be(ioread32be(&regs->maccfg1) & ~MACCFG1_TX_FLOW,
&regs->maccfg1);
return 0;
}
int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en)
{
struct dtsec_regs __iomem *regs = dtsec->regs;
u32 tmp;
if (!is_init_done(dtsec->dtsec_drv_param))
return -EINVAL;
tmp = ioread32be(&regs->maccfg1);
if (en)
tmp |= MACCFG1_RX_FLOW;
else
tmp &= ~MACCFG1_RX_FLOW;
iowrite32be(tmp, &regs->maccfg1);
return 0;
}
int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr)
{
if (!is_init_done(dtsec->dtsec_drv_param))
return -EINVAL;
/* 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));
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(&regs->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_KERNEL);
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_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(&regs->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->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->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(&regs->rctrl);
if (new_val)
tmp |= RCTRL_UPROM;
else
tmp &= ~RCTRL_UPROM;
iowrite32be(tmp, &regs->rctrl);
/* Set multicast promiscuous */
tmp = ioread32be(&regs->rctrl);
if (new_val)
tmp |= RCTRL_MPROM;
else
tmp &= ~RCTRL_MPROM;
iowrite32be(tmp, &regs->rctrl);
return 0;
}
int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed)
{
struct dtsec_regs __iomem *regs = dtsec->regs;
u32 tmp;
if (!is_init_done(dtsec->dtsec_drv_param))
return -EINVAL;
tmp = ioread32be(&regs->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, &regs->maccfg2);
tmp = ioread32be(&regs->ecntrl);
if (speed == SPEED_100)
tmp |= DTSEC_ECNTRL_R100M;
else
tmp &= ~DTSEC_ECNTRL_R100M;
iowrite32be(tmp, &regs->ecntrl);
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(&regs->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(&regs->imask) | bit_mask,
&regs->imask);
else
iowrite32be(ioread32be(&regs->imask) & ~bit_mask,
&regs->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_exeption = true;
iowrite32be(ioread32be(&regs->tmr_pemask) |
TMR_PEMASK_TSREEN,
&regs->tmr_pemask);
} else {
dtsec->en_tsu_err_exeption = false;
iowrite32be(ioread32be(&regs->tmr_pemask) &
~TMR_PEMASK_TSREEN,
&regs->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;
int err;
u16 max_frm_ln;
enet_addr_t eth_addr;
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;
MAKE_ENET_ADDR_FROM_UINT64(dtsec->addr, eth_addr);
err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if,
dtsec->max_speed, (u8 *)eth_addr, dtsec->exceptions,
dtsec->tbiphy->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(&regs->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_exeption = 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");
put_device(&dtsec->tbiphy->dev);
goto err_dtsec_drv_param;
}
put_device(&dtsec->tbiphy->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;
}
/*
* 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);
#endif /* __DTSEC_H */
/*
* 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;
/* MDIO exceptions interrupt source - not valid for all
* MACs; MUST be set to 'NO_IRQ' for MACs that don't have
* mdio-irq, or for polling
*/
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_array(size, sizeof(struct eth_hash_t *), 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 */
/*
* 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/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_XGMII 0x00000000 /* 30-31 XGMII (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;
};
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, &regs->mac_addr0.mac_addr_l);
iowrite32be(tmp1, &regs->mac_addr0.mac_addr_u);
} else {
iowrite32be(tmp0, &regs->mac_addr[paddr_num - 1].mac_addr_l);
iowrite32be(tmp1, &regs->mac_addr[paddr_num - 1].mac_addr_u);
}
}
static int reset(struct memac_regs __iomem *regs)
{
u32 tmp;
int count;
tmp = ioread32be(&regs->command_config);
tmp |= CMD_CFG_SW_RESET;
iowrite32be(tmp, &regs->command_config);
count = 100;
do {
udelay(1);
} while ((ioread32be(&regs->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(&regs->imask);
if (enable)
tmp |= val;
else
tmp &= ~val;
iowrite32be(tmp, &regs->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, &regs->command_config);
/* Max Frame Length */
iowrite32be((u32)cfg->max_frame_length, &regs->maxfrm);
/* Pause Time */
iowrite32be((u32)cfg->pause_quanta, &regs->pause_quanta[0]);
iowrite32be((u32)0, &regs->pause_thresh[0]);
/* IF_MODE */
tmp = 0;
switch (phy_if) {
case PHY_INTERFACE_MODE_XGMII:
tmp |= IF_MODE_XGMII;
break;
default:
tmp |= IF_MODE_GMII;
if (phy_if == PHY_INTERFACE_MODE_RGMII)
tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO;
}
iowrite32be(tmp, &regs->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, &regs->tx_fifo_sections);
/* clear all pending events and set-up interrupts */
iowrite32be(0xffffffff, &regs->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;
/* 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: /* fallthrough */
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->addr == 0) {
pr_err("Ethernet MAC must have a valid MAC address\n");
return -EINVAL;
}
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(&regs->ievent);
imask = ioread32be(&regs->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, &regs->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(&regs->ievent);
imask = ioread32be(&regs->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, &regs->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(&regs->command_config);
if (mode & COMM_MODE_RX)
tmp |= CMD_CFG_RX_EN;
if (mode & COMM_MODE_TX)
tmp |= CMD_CFG_TX_EN;
iowrite32be(tmp, &regs->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(&regs->command_config);
if (mode & COMM_MODE_RX)
tmp &= ~CMD_CFG_RX_EN;
if (mode & COMM_MODE_TX)
tmp &= ~CMD_CFG_TX_EN;
iowrite32be(tmp, &regs->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(&regs->command_config);
if (new_val)
tmp |= CMD_CFG_PROMIS_EN;
else
tmp &= ~CMD_CFG_PROMIS_EN;
iowrite32be(tmp, &regs->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(&regs->if_mode);
/* Set full duplex */
tmp &= ~IF_MODE_HD;
if (memac->phy_if == PHY_INTERFACE_MODE_RGMII) {
/* 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, &regs->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(&regs->tx_fifo_sections);
GET_TX_EMPTY_DEFAULT_VALUE(tmp);
iowrite32be(tmp, &regs->tx_fifo_sections);
tmp = ioread32be(&regs->command_config);
tmp &= ~CMD_CFG_PFC_MODE;
priority = 0;
iowrite32be(tmp, &regs->command_config);
tmp = ioread32be(&regs->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, &regs->pause_quanta[priority / 2]);
tmp = ioread32be(&regs->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, &regs->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(&regs->command_config);
if (en)
tmp &= ~CMD_CFG_PAUSE_IGNORE;
else
tmp |= CMD_CFG_PAUSE_IGNORE;
iowrite32be(tmp, &regs->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_KERNEL);
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, &regs->hashtable_ctrl);
return 0;
}
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->addr == addr) {
list_del_init(&hash_entry->node);
kfree(hash_entry);
break;
}
}
if (list_empty(&memac->multicast_addr_hash->lsts[hash]))
iowrite32be(hash & ~HASH_CTRL_MCAST_EN, &regs->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 */
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->addr;
qsmgii_phy_addr = (u8)((phy_addr << 2) | i);
memac->pcsphy->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->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);
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) {
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;
}
/*
* 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>
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);
#endif /* __MEMAC_H */
/*
* 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.
*/
int 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, u32 offset, size_t size)
{
unsigned long addr = fman_muram_offset_to_vbase(muram, offset);
gen_pool_free(muram->pool, addr, size);
}
/*
* 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);
int fman_muram_alloc(struct muram_info *muram, size_t size);
void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size);
#endif /* __FM_MURAM_EXT */
/*
* 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_port.h"
#include "fman.h"
#include "fman_sp.h"
#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>
/* 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
/* 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_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 */
};
/* 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;
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 second_largest_buf_size;
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_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, &regs->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, &regs->fmbm_rfp);
if (cfg->excessive_threshold_register)
/* always allow access to the extra resources */
iowrite32be(BMI_RX_FIFO_THRESHOLD_ETHE, &regs->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, &regs->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, &regs->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, &regs->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, &regs->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, &regs->fmbm_rfca);
/* NIA */
tmp = (u32)cfg->rx_fd_bits << BMI_NEXT_ENG_FD_BITS_SHIFT;
tmp |= NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME;
iowrite32be(tmp, &regs->fmbm_rfne);
/* Enqueue NIA */
iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, &regs->fmbm_rfene);
/* Default/error queues */
iowrite32be((cfg->dflt_fqid & DFLT_FQ_ID), &regs->fmbm_rfqid);
iowrite32be((cfg->err_fqid & DFLT_FQ_ID), &regs->fmbm_refqid);
/* Discard/error masks */
iowrite32be(cfg->discard_mask, &regs->fmbm_rfsdm);
iowrite32be(cfg->err_mask, &regs->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, &regs->fmbm_tcfg);
/* DMA attributes */
tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT;
iowrite32be(tmp, &regs->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, &regs->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, &regs->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, &regs->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, &regs->fmbm_tfca);
/* Dequeue NIA + enqueue NIA */
iowrite32be(NIA_ENG_QMI_DEQ, &regs->fmbm_tfdne);
iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, &regs->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, &regs->fmbm_tfne);
if (!cfg->dflt_fqid && cfg->dont_release_buf) {
iowrite32be(DFLT_FQ_ID, &regs->fmbm_tcfqid);
iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
&regs->fmbm_tfene);
if (cfg->fmbm_tfne_has_features)
iowrite32be(ioread32be(&regs->fmbm_tfne) & ~BMI_EBD_EN,
&regs->fmbm_tfne);
}
/* Confirmation/error queues */
if (cfg->dflt_fqid || !cfg->dont_release_buf)
iowrite32be(cfg->dflt_fqid & DFLT_FQ_ID, &regs->fmbm_tcfqid);
iowrite32be((cfg->err_fqid & DFLT_FQ_ID), &regs->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, &regs->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,
&regs->fmqm_pnen);
/* Dequeue NIA */
iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX, &regs->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, &regs->fmqm_pndc);
return 0;
}
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);
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);
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]];
port->rx_pools_params.second_largest_buf_size =
sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 2]];
/* 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;
}
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)
goto err_params;
/* 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);
case FMAN_PORT_TYPE_TX:
set_tx_dflt_cfg(port, params, &port->dts_params);
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->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
*/
if (port->rev_info.major >= 6) {
u32 reg;
reg = 0x00001013;
iowrite32be(reg, &port->bmi_regs->tx.fmbm_tfp);
}
}
return 0;
err_port_cfg:
kfree(port->cfg);
err_params:
kfree(port);
return -EINVAL;
}
EXPORT_SYMBOL(fman_port_config);
/**
* 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_cfg *cfg;
int err;
struct fman_port_init_params params;
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(&params, 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, &params);
if (err)
return err;
err = init_low_level_driver(port);
if (err)
return err;
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, 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, 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);
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 resource res;
struct resource *dev_res;
const u32 *u32_prop;
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;
}
fman = dev_get_drvdata(&of_find_device_by_node(fm_node)->dev);
of_node_put(fm_node);
if (!fman) {
err = -EINVAL;
goto return_err;
}
u32_prop = (const u32 *)of_get_property(port_node, "cell-index", &lenp);
if (!u32_prop) {
dev_err(port->dev, "%s: of_get_property(%s, cell-index) failed\n",
__func__, port_node->full_name);
err = -EINVAL;
goto return_err;
}
if (WARN_ON(lenp != sizeof(u32))) {
err = -EINVAL;
goto return_err;
}
port_id = (u8)fdt32_to_cpu(u32_prop[0]);
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;
u32_prop = (const u32 *)of_get_property(port_node,
"fsl,fman-10g-port",
&lenp);
if (u32_prop)
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;
u32_prop = (const u32 *)of_get_property(port_node,
"fsl,fman-10g-port", &lenp);
if (u32_prop)
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 return_err;
}
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 return_err;
}
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 return_err;
}
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 == 0)
dev_err(port->dev, "%s: devm_ioremap() failed\n", __func__);
dev_set_drvdata(&of_dev->dev, port);
return 0;
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,
};
builtin_platform_driver(fman_port_driver);
/*
* 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. */
/* 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);
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);
struct fman_port *fman_port_bind(struct device *dev);
#endif /* __FMAN_PORT_H */
/*
* 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;
}
}
}
}
}
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;
}
/*
* 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 */
/*
* 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_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;
};
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, &regs->mac_addr_0);
iowrite32be(tmp1, &regs->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, &regs->command_config);
/* Max Frame Length */
iowrite32be((u32)cfg->max_frame_length, &regs->maxfrm);
/* Pause Time */
iowrite32be(cfg->pause_quant, &regs->pause_quant);
/* clear all pending events and set-up interrupts */
iowrite32be(0xffffffff, &regs->ievent);
iowrite32be(ioread32be(&regs->imask) | exception_mask, &regs->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->addr == 0) {
pr_err("Ethernet 10G MAC Must have valid MAC Address\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(&regs->ievent) &
~(TGEC_IMASK_MDIO_SCAN_EVENT |
TGEC_IMASK_MDIO_CMD_CMPL);
event &= ioread32be(&regs->imask);
iowrite32be(event, &regs->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(&regs->command_config);
if (mode & COMM_MODE_RX)
tmp |= CMD_CFG_RX_EN;
if (mode & COMM_MODE_TX)
tmp |= CMD_CFG_TX_EN;
iowrite32be(tmp, &regs->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(&regs->command_config);
if (mode & COMM_MODE_RX)
tmp &= ~CMD_CFG_RX_EN;
if (mode & COMM_MODE_TX)
tmp &= ~CMD_CFG_TX_EN;
iowrite32be(tmp, &regs->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(&regs->command_config);
if (new_val)
tmp |= CMD_CFG_PROMIS_EN;
else
tmp &= ~CMD_CFG_PROMIS_EN;
iowrite32be(tmp, &regs->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, &regs->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(&regs->command_config);
if (!en)
tmp |= CMD_CFG_PAUSE_IGNORE;
else
tmp &= ~CMD_CFG_PAUSE_IGNORE;
iowrite32be(tmp, &regs->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_KERNEL);
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), &regs->hashtable_ctrl);
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->addr == addr) {
list_del_init(&hash_entry->node);
kfree(hash_entry);
break;
}
}
if (list_empty(&tgec->multicast_addr_hash->lsts[hash]))
iowrite32be((hash & ~TGEC_HASH_MCAST_EN),
&regs->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(&regs->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(&regs->imask) | bit_mask, &regs->imask);
else
iowrite32be(ioread32be(&regs->imask) & ~bit_mask, &regs->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;
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(&regs->tx_ipg_len) &
~TGEC_TX_IPG_LENGTH_MASK) | 12;
iowrite32be(tmp, &regs->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);
if (tgec->cfg)
tgec->cfg = NULL;
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;
}
/*
* 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);
#endif /* __TGEC_H */
/* 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;
phy_interface_t phy_if;
struct fman *fman;
struct device_node *phy_node;
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 void 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));
memcpy(&params->addr, mac_dev->addr, sizeof(mac_dev->addr));
params->max_speed = priv->max_speed;
params->phy_if = priv->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;
}
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;
set_fman_mac_params(mac_dev, &params);
mac_dev->fman_mac = tgec_config(&params);
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;
set_fman_mac_params(mac_dev, &params);
mac_dev->fman_mac = dtsec_config(&params);
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;
set_fman_mac_params(mac_dev, &params);
if (priv->max_speed == SPEED_10000)
params.phy_if = PHY_INTERFACE_MODE_XGMII;
mac_dev->fman_mac = memac_config(&params);
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: Return value for RX setting
* @tx: 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 = 0;
if (phy_dev->advertising & ADVERTISED_Pause)
lcl_adv |= ADVERTISE_PAUSE_CAP;
if (phy_dev->advertising & ADVERTISED_Asym_Pause)
lcl_adv |= ADVERTISE_PAUSE_ASYM;
/* 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 net_device *net_dev)
{
}
static void adjust_link_dtsec(struct net_device *net_dev)
{
struct device *dev = net_dev->dev.parent;
struct dpaa_eth_data *eth_data = dev->platform_data;
struct mac_device *mac_dev = eth_data->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)
netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err);
}
static void adjust_link_memac(struct net_device *net_dev)
{
struct device *dev = net_dev->dev.parent;
struct dpaa_eth_data *eth_data = dev->platform_data;
struct mac_device *mac_dev = eth_data->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)
netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err);
}
/* Initializes driver's PHY state, and attaches to the PHY.
* Returns 0 on success.
*/
static int init_phy(struct net_device *net_dev,
struct mac_device *mac_dev,
void (*adj_lnk)(struct net_device *))
{
struct phy_device *phy_dev;
struct mac_priv_s *priv = mac_dev->priv;
phy_dev = of_phy_connect(net_dev, priv->phy_node, adj_lnk, 0,
priv->phy_if);
if (!phy_dev) {
netdev_err(net_dev, "Could not connect to PHY\n");
return -ENODEV;
}
/* Remove any features not supported by the controller */
phy_dev->supported &= mac_dev->if_support;
/* Enable the symmetric and asymmetric PAUSE frame advertisements,
* as most of the PHY drivers do not enable them by default.
*/
phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause);
phy_dev->advertising = phy_dev->supported;
mac_dev->phy_dev = phy_dev;
return 0;
}
static int dtsec_init_phy(struct net_device *net_dev,
struct mac_device *mac_dev)
{
return init_phy(net_dev, mac_dev, &adjust_link_dtsec);
}
static int tgec_init_phy(struct net_device *net_dev,
struct mac_device *mac_dev)
{
return init_phy(net_dev, mac_dev, adjust_link_void);
}
static int memac_init_phy(struct net_device *net_dev,
struct mac_device *mac_dev)
{
return init_phy(net_dev, mac_dev, &adjust_link_memac);
}
static void setup_dtsec(struct mac_device *mac_dev)
{
mac_dev->init_phy = dtsec_init_phy;
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_multi = set_multi;
mac_dev->start = start;
mac_dev->stop = stop;
mac_dev->priv->enable = dtsec_enable;
mac_dev->priv->disable = dtsec_disable;
}
static void setup_tgec(struct mac_device *mac_dev)
{
mac_dev->init_phy = tgec_init_phy;
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_multi = set_multi;
mac_dev->start = start;
mac_dev->stop = stop;
mac_dev->priv->enable = tgec_enable;
mac_dev->priv->disable = tgec_disable;
}
static void setup_memac(struct mac_device *mac_dev)
{
mac_dev->init_phy = memac_init_phy;
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_multi = set_multi;
mac_dev->start = start;
mac_dev->stop = stop;
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 char phy_str[][11] = {
[PHY_INTERFACE_MODE_MII] = "mii",
[PHY_INTERFACE_MODE_GMII] = "gmii",
[PHY_INTERFACE_MODE_SGMII] = "sgmii",
[PHY_INTERFACE_MODE_TBI] = "tbi",
[PHY_INTERFACE_MODE_RMII] = "rmii",
[PHY_INTERFACE_MODE_RGMII] = "rgmii",
[PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id",
[PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid",
[PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid",
[PHY_INTERFACE_MODE_RTBI] = "rtbi",
[PHY_INTERFACE_MODE_XGMII] = "xgmii"
};
static phy_interface_t __pure __attribute__((nonnull)) str2phy(const char *str)
{
int i;
for (i = 0; i < ARRAY_SIZE(phy_str); i++)
if (strcmp(str, phy_str[i]) == 0)
return (phy_interface_t)i;
return PHY_INTERFACE_MODE_MII;
}
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_XGMII] = SPEED_10000
};
static struct platform_device *dpaa_eth_add_device(int fman_id,
struct mac_device *mac_dev,
struct device_node *node)
{
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;
data.mac_node = node;
mutex_lock(&eth_lock);
pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt);
if (!pdev) {
ret = -ENOMEM;
goto no_mem;
}
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(&eth_lock);
return pdev;
err:
platform_device_put(pdev);
no_mem:
mutex_unlock(&eth_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, lenp, 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;
const char *char_prop;
const u32 *u32_prop;
u8 fman_id;
dev = &_of_dev->dev;
mac_node = dev->of_node;
mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL);
if (!mac_dev) {
err = -ENOMEM;
dev_err(dev, "devm_kzalloc() = %d\n", err);
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 (%s) contains unsupported MAC\n",
mac_node->full_name);
err = -EINVAL;
goto _return;
}
/* Register mac_dev */
dev_set_drvdata(dev, mac_dev);
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(%s) failed\n",
mac_node->full_name);
err = -EINVAL;
goto _return_dev_set_drvdata;
}
of_dev = of_find_device_by_node(dev_node);
if (!of_dev) {
dev_err(dev, "of_find_device_by_node(%s) failed\n",
dev_node->full_name);
err = -EINVAL;
goto _return_of_node_put;
}
/* Get the FMan cell-index */
u32_prop = of_get_property(dev_node, "cell-index", &lenp);
if (!u32_prop) {
dev_err(dev, "of_get_property(%s, cell-index) failed\n",
dev_node->full_name);
err = -EINVAL;
goto _return_of_node_put;
}
WARN_ON(lenp != sizeof(u32));
/* cell-index 0 => FMan id 1 */
fman_id = (u8)(fdt32_to_cpu(u32_prop[0]) + 1);
priv->fman = fman_bind(&of_dev->dev);
if (!priv->fman) {
dev_err(dev, "fman_bind(%s) failed\n", dev_node->full_name);
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(%s) = %d\n",
mac_node->full_name, err);
goto _return_dev_set_drvdata;
}
mac_dev->res = __devm_request_region(dev,
fman_get_mem_region(priv->fman),
res.start, res.end + 1 - res.start,
"mac");
if (!mac_dev->res) {
dev_err(dev, "__devm_request_mem_region(mac) failed\n");
err = -EBUSY;
goto _return_dev_set_drvdata;
}
priv->vaddr = devm_ioremap(dev, mac_dev->res->start,
mac_dev->res->end + 1 - mac_dev->res->start);
if (!priv->vaddr) {
dev_err(dev, "devm_ioremap() failed\n");
err = -EIO;
goto _return_dev_set_drvdata;
}
if (!of_device_is_available(mac_node)) {
devm_iounmap(dev, priv->vaddr);
__devm_release_region(dev, fman_get_mem_region(priv->fman),
res.start, res.end + 1 - res.start);
devm_kfree(dev, mac_dev);
dev_set_drvdata(dev, NULL);
return -ENODEV;
}
/* Get the cell-index */
u32_prop = of_get_property(mac_node, "cell-index", &lenp);
if (!u32_prop) {
dev_err(dev, "of_get_property(%s, cell-index) failed\n",
mac_node->full_name);
err = -EINVAL;
goto _return_dev_set_drvdata;
}
WARN_ON(lenp != sizeof(u32));
priv->cell_index = (u8)fdt32_to_cpu(u32_prop[0]);
/* Get the MAC address */
mac_addr = of_get_mac_address(mac_node);
if (!mac_addr) {
dev_err(dev, "of_get_mac_address(%s) failed\n",
mac_node->full_name);
err = -EINVAL;
goto _return_dev_set_drvdata;
}
memcpy(mac_dev->addr, mac_addr, sizeof(mac_dev->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(%s, fsl,fman-ports) failed\n",
mac_node->full_name);
err = nph;
goto _return_dev_set_drvdata;
}
if (nph != ARRAY_SIZE(mac_dev->port)) {
dev_err(dev, "Not supported number of fman-ports handles of mac node %s from device tree\n",
mac_node->full_name);
err = -EINVAL;
goto _return_dev_set_drvdata;
}
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(%s, fsl,fman-ports) failed\n",
mac_node->full_name);
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(%s) failed\n",
dev_node->full_name);
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(%s) failed\n",
dev_node->full_name);
err = -EINVAL;
goto _return_of_node_put;
}
of_node_put(dev_node);
}
/* Get the PHY connection type */
char_prop = (const char *)of_get_property(mac_node,
"phy-connection-type", NULL);
if (!char_prop) {
dev_warn(dev,
"of_get_property(%s, phy-connection-type) failed. Defaulting to MII\n",
mac_node->full_name);
priv->phy_if = PHY_INTERFACE_MODE_MII;
} else {
priv->phy_if = str2phy(char_prop);
}
priv->speed = phy2speed[priv->phy_if];
priv->max_speed = priv->speed;
mac_dev->if_support = DTSEC_SUPPORTED;
/* We don't support half-duplex in SGMII mode */
if (priv->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 (priv->phy_if == PHY_INTERFACE_MODE_XGMII)
mac_dev->if_support = SUPPORTED_10000baseT_Full;
/* Get the rest of the PHY information */
priv->phy_node = of_parse_phandle(mac_node, "phy-handle", 0);
if (!priv->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_dev_set_drvdata;
priv->fixed_link = kzalloc(sizeof(*priv->fixed_link),
GFP_KERNEL);
if (!priv->fixed_link)
goto _return_dev_set_drvdata;
priv->phy_node = of_node_get(mac_node);
phy = of_phy_find_device(priv->phy_node);
if (!phy)
goto _return_dev_set_drvdata;
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;
}
err = mac_dev->init(mac_dev);
if (err < 0) {
dev_err(dev, "mac_dev->init() = %d\n", err);
of_node_put(priv->phy_node);
goto _return_dev_set_drvdata;
}
/* 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);
dev_info(dev, "FMan MAC address: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2],
mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5]);
priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev, mac_node);
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_dev_set_drvdata:
kfree(priv->fixed_link);
kfree(priv);
dev_set_drvdata(dev, NULL);
_return:
return err;
}
static struct platform_driver mac_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = mac_match,
},
.probe = mac_probe,
};
builtin_platform_driver(mac_driver);
/* 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;
bool autoneg_pause;
bool rx_pause_req;
bool tx_pause_req;
bool rx_pause_active;
bool tx_pause_active;
bool promisc;
int (*init_phy)(struct net_device *net_dev, struct mac_device *mac_dev);
int (*init)(struct mac_device *mac_dev);
int (*start)(struct mac_device *mac_dev);
int (*stop)(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_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 device_node *mac_node;
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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment