From: Iordache Florinel-R70177 <florinel.iorda...@nxp.com> Add support for the FMan Keygen with a hardcoded scheme to spread incoming traffic on a FQ range based on source and destination IPs and ports.
Signed-off-by: Iordache Florinel <florinel.iorda...@nxp.com> Signed-off-by: Madalin Bucur <madalin.bu...@nxp.com> --- drivers/net/ethernet/freescale/fman/Makefile | 2 +- drivers/net/ethernet/freescale/fman/fman.c | 8 + drivers/net/ethernet/freescale/fman/fman.h | 2 + drivers/net/ethernet/freescale/fman/fman_keygen.c | 783 ++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_keygen.h | 46 ++ drivers/net/ethernet/freescale/fman/fman_port.c | 40 +- drivers/net/ethernet/freescale/fman/fman_port.h | 5 + 7 files changed, 884 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/freescale/fman/fman_keygen.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_keygen.h diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index 6049177..2c38119 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_FSL_FMAN) += fsl_fman.o obj-$(CONFIG_FSL_FMAN) += fsl_fman_port.o obj-$(CONFIG_FSL_FMAN) += fsl_mac.o -fsl_fman-objs := fman_muram.o fman.o fman_sp.o +fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_keygen.o fsl_fman_port-objs := fman_port.o fsl_mac-objs:= mac.o fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 8179cc1..f420dac 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -45,6 +45,7 @@ #include "fman.h" #include "fman_muram.h" +#include "fman_keygen.h" /* General defines */ #define FMAN_LIODN_TBL 64 /* size of LIODN table */ @@ -56,6 +57,7 @@ /* Modules registers offsets */ #define BMI_OFFSET 0x00080000 #define QMI_OFFSET 0x00080400 +#define KG_OFFSET 0x000C1000 #define DMA_OFFSET 0x000C2000 #define FPM_OFFSET 0x000C3000 #define IMEM_OFFSET 0x000C4000 @@ -1737,6 +1739,7 @@ static int fman_config(struct fman *fman) fman->qmi_regs = base_addr + QMI_OFFSET; fman->dma_regs = base_addr + DMA_OFFSET; fman->hwp_regs = base_addr + HWP_OFFSET; + fman->kg_regs = base_addr + KG_OFFSET; fman->base_addr = base_addr; spin_lock_init(&fman->spinlock); @@ -2009,6 +2012,11 @@ static int fman_init(struct fman *fman) /* Init HW Parser */ hwp_init(fman->hwp_regs); + /* Init KeyGen */ + fman->keygen = keygen_init(fman->kg_regs); + if (!fman->keygen) + return -EINVAL; + err = enable(fman, cfg); if (err != 0) return err; diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h index 1015dac..bfa02e0 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -328,6 +328,7 @@ struct fman { struct fman_qmi_regs __iomem *qmi_regs; struct fman_dma_regs __iomem *dma_regs; struct fman_hwp_regs __iomem *hwp_regs; + struct fman_kg_regs __iomem *kg_regs; fman_exceptions_cb *exception_cb; fman_bus_error_cb *bus_error_cb; /* Spinlock for FMan use */ @@ -336,6 +337,7 @@ struct fman { struct fman_cfg *cfg; struct muram_info *muram; + struct fman_keygen *keygen; /* cam section in muram */ unsigned long cam_offset; size_t cam_size; diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.c b/drivers/net/ethernet/freescale/fman/fman_keygen.c new file mode 100644 index 0000000..f54da3c --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.c @@ -0,0 +1,783 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> + +#include "fman_keygen.h" + +/* Maximum number of HW Ports */ +#define FMAN_MAX_NUM_OF_HW_PORTS 64 + +/* Maximum number of KeyGen Schemes */ +#define FM_KG_MAX_NUM_OF_SCHEMES 32 + +/* Number of generic KeyGen Generic Extract Command Registers */ +#define FM_KG_NUM_OF_GENERIC_REGS 8 + +/* Dummy port ID */ +#define DUMMY_PORT_ID 0 + +/* Select Scheme Value Register */ +#define KG_SCH_DEF_USE_KGSE_DV_0 2 +#define KG_SCH_DEF_USE_KGSE_DV_1 3 + +/* Registers Shifting values */ +#define FM_KG_KGAR_NUM_SHIFT 16 +#define KG_SCH_DEF_L4_PORT_SHIFT 8 +#define KG_SCH_DEF_IP_ADDR_SHIFT 18 +#define KG_SCH_HASH_CONFIG_SHIFT_SHIFT 24 + +/* KeyGen Registers bit field masks: */ + +/* Enable bit field mask for KeyGen General Configuration Register */ +#define FM_KG_KGGCR_EN 0x80000000 + +/* KeyGen Global Registers bit field masks */ +#define FM_KG_KGAR_GO 0x80000000 +#define FM_KG_KGAR_READ 0x40000000 +#define FM_KG_KGAR_WRITE 0x00000000 +#define FM_KG_KGAR_SEL_SCHEME_ENTRY 0x00000000 +#define FM_KG_KGAR_SCM_WSEL_UPDATE_CNT 0x00008000 + +#define FM_KG_KGAR_ERR 0x20000000 +#define FM_KG_KGAR_SEL_CLS_PLAN_ENTRY 0x01000000 +#define FM_KG_KGAR_SEL_PORT_ENTRY 0x02000000 +#define FM_KG_KGAR_SEL_PORT_WSEL_SP 0x00008000 +#define FM_KG_KGAR_SEL_PORT_WSEL_CPP 0x00004000 + +/* Error events exceptions */ +#define FM_EX_KG_DOUBLE_ECC 0x80000000 +#define FM_EX_KG_KEYSIZE_OVERFLOW 0x40000000 + +/* Scheme Registers bit field masks */ +#define KG_SCH_MODE_EN 0x80000000 +#define KG_SCH_VSP_NO_KSP_EN 0x80000000 +#define KG_SCH_HASH_CONFIG_SYM 0x40000000 + +/* Known Protocol field codes */ +#define KG_SCH_KN_PORT_ID 0x80000000 +#define KG_SCH_KN_MACDST 0x40000000 +#define KG_SCH_KN_MACSRC 0x20000000 +#define KG_SCH_KN_TCI1 0x10000000 +#define KG_SCH_KN_TCI2 0x08000000 +#define KG_SCH_KN_ETYPE 0x04000000 +#define KG_SCH_KN_PPPSID 0x02000000 +#define KG_SCH_KN_PPPID 0x01000000 +#define KG_SCH_KN_MPLS1 0x00800000 +#define KG_SCH_KN_MPLS2 0x00400000 +#define KG_SCH_KN_MPLS_LAST 0x00200000 +#define KG_SCH_KN_IPSRC1 0x00100000 +#define KG_SCH_KN_IPDST1 0x00080000 +#define KG_SCH_KN_PTYPE1 0x00040000 +#define KG_SCH_KN_IPTOS_TC1 0x00020000 +#define KG_SCH_KN_IPV6FL1 0x00010000 +#define KG_SCH_KN_IPSRC2 0x00008000 +#define KG_SCH_KN_IPDST2 0x00004000 +#define KG_SCH_KN_PTYPE2 0x00002000 +#define KG_SCH_KN_IPTOS_TC2 0x00001000 +#define KG_SCH_KN_IPV6FL2 0x00000800 +#define KG_SCH_KN_GREPTYPE 0x00000400 +#define KG_SCH_KN_IPSEC_SPI 0x00000200 +#define KG_SCH_KN_IPSEC_NH 0x00000100 +#define KG_SCH_KN_IPPID 0x00000080 +#define KG_SCH_KN_L4PSRC 0x00000004 +#define KG_SCH_KN_L4PDST 0x00000002 +#define KG_SCH_KN_TFLG 0x00000001 + +/* NIA values */ +#define NIA_ENG_BMI 0x00500000 +#define NIA_BMI_AC_ENQ_FRAME 0x00000002 +#define ENQUEUE_KG_DFLT_NIA (NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME) + +/* Hard-coded configuration: + * These values are used as hard-coded values for KeyGen configuration + * and they replace user selections for this hard-coded version + */ + +/* Hash distribution shift */ +#define DEFAULT_HASH_DIST_FQID_SHIFT 0 + +/* Hash shift */ +#define DEFAULT_HASH_SHIFT 0 + +/* Symmetric hash usage: + * Warning: + * - the value for symmetric hash usage must be in accordance with hash + * key defined below + * - according to tests performed, spreading is not working if symmetric + * hash is set on true + * So ultimately symmetric hash functionality should be always disabled: + */ +#define DEFAULT_SYMMETRIC_HASH false + +/* Hash Key extraction fields: */ +#define DEFAULT_HASH_KEY_EXTRACT_FIELDS \ + (KG_SCH_KN_IPSRC1 | KG_SCH_KN_IPDST1 | \ + KG_SCH_KN_L4PSRC | KG_SCH_KN_L4PDST) + +/* Default values to be used as hash key in case IPv4 or L4 (TCP, UDP) + * don't exist in the frame + */ +/* Default IPv4 address */ +#define DEFAULT_HASH_KEY_IPv4_ADDR 0x0A0A0A0A +/* Default L4 port */ +#define DEFAULT_HASH_KEY_L4_PORT 0x0B0B0B0B + +/* KeyGen Memory Mapped Registers: */ + +/* Scheme Configuration RAM Registers */ +struct fman_kg_scheme_regs { + u32 kgse_mode; /* 0x100: MODE */ + u32 kgse_ekfc; /* 0x104: Extract Known Fields Command */ + u32 kgse_ekdv; /* 0x108: Extract Known Default Value */ + u32 kgse_bmch; /* 0x10C: Bit Mask Command High */ + u32 kgse_bmcl; /* 0x110: Bit Mask Command Low */ + u32 kgse_fqb; /* 0x114: Frame Queue Base */ + u32 kgse_hc; /* 0x118: Hash Command */ + u32 kgse_ppc; /* 0x11C: Policer Profile Command */ + u32 kgse_gec[FM_KG_NUM_OF_GENERIC_REGS]; + /* 0x120: Generic Extract Command */ + u32 kgse_spc; + /* 0x140: KeyGen Scheme Entry Statistic Packet Counter */ + u32 kgse_dv0; /* 0x144: KeyGen Scheme Entry Default Value 0 */ + u32 kgse_dv1; /* 0x148: KeyGen Scheme Entry Default Value 1 */ + u32 kgse_ccbs; + /* 0x14C: KeyGen Scheme Entry Coarse Classification Bit*/ + u32 kgse_mv; /* 0x150: KeyGen Scheme Entry Match vector */ + u32 kgse_om; /* 0x154: KeyGen Scheme Entry Operation Mode bits */ + u32 kgse_vsp; + /* 0x158: KeyGen Scheme Entry Virtual Storage Profile */ +}; + +/* Port Partition Configuration Registers */ +struct fman_kg_pe_regs { + u32 fmkg_pe_sp; /* 0x100: KeyGen Port entry Scheme Partition */ + u32 fmkg_pe_cpp; + /* 0x104: KeyGen Port Entry Classification Plan Partition */ +}; + +/* General Configuration and Status Registers + * Global Statistic Counters + * KeyGen Global Registers + */ +struct fman_kg_regs { + u32 fmkg_gcr; /* 0x000: KeyGen General Configuration Register */ + u32 res004; /* 0x004: Reserved */ + u32 res008; /* 0x008: Reserved */ + u32 fmkg_eer; /* 0x00C: KeyGen Error Event Register */ + u32 fmkg_eeer; /* 0x010: KeyGen Error Event Enable Register */ + u32 res014; /* 0x014: Reserved */ + u32 res018; /* 0x018: Reserved */ + u32 fmkg_seer; /* 0x01C: KeyGen Scheme Error Event Register */ + u32 fmkg_seeer; /* 0x020: KeyGen Scheme Error Event Enable Register */ + u32 fmkg_gsr; /* 0x024: KeyGen Global Status Register */ + u32 fmkg_tpc; /* 0x028: Total Packet Counter Register */ + u32 fmkg_serc; /* 0x02C: Soft Error Capture Register */ + u32 res030[4]; /* 0x030: Reserved */ + u32 fmkg_fdor; /* 0x034: Frame Data Offset Register */ + u32 fmkg_gdv0r; /* 0x038: Global Default Value Register 0 */ + u32 fmkg_gdv1r; /* 0x03C: Global Default Value Register 1 */ + u32 res04c[6]; /* 0x040: Reserved */ + u32 fmkg_feer; /* 0x044: Force Error Event Register */ + u32 res068[38]; /* 0x048: Reserved */ + union { + u32 fmkg_indirect[63]; /* 0x100: Indirect Access Registers */ + struct fman_kg_scheme_regs fmkg_sch; /* Scheme Registers */ + struct fman_kg_pe_regs fmkg_pe; /* Port Partition Registers */ + }; + u32 fmkg_ar; /* 0x1FC: KeyGen Action Register */ +}; + +/* KeyGen Scheme data */ +struct keygen_scheme { + bool used; /* Specifies if this scheme is used */ + u8 hw_port_id; + /* Hardware port ID + * schemes sharing between multiple ports is not + * currently supported + * so we have only one port id bound to a scheme + */ + u32 base_fqid; + /* Base FQID: + * Must be between 1 and 2^24-1 + * If hash is used and an even distribution is + * expected according to hash_fqid_count, + * base_fqid must be aligned to hash_fqid_count + */ + u32 hash_fqid_count; + /* FQ range for hash distribution: + * Must be a power of 2 + * Represents the range of queues for spreading + */ + bool use_hashing; /* Usage of Hashing and spreading over FQ */ + bool symmetric_hash; /* Symmetric Hash option usage */ + u8 hashShift; + /* Hash result right shift. + * Select the 24 bits out of the 64 hash result. + * 0 means using the 24 LSB's, otherwise + * use the 24 LSB's after shifting right + */ + u32 match_vector; /* Match Vector */ +}; + +/* KeyGen driver data */ +struct fman_keygen { + struct keygen_scheme schemes[FM_KG_MAX_NUM_OF_SCHEMES]; + /* Array of schemes */ + struct fman_kg_regs __iomem *keygen_regs; /* KeyGen registers */ +}; + +/* keygen_write_ar_wait + * + * Write Action Register with specified value, wait for GO bit field to be + * idle and then read the error + * + * regs: KeyGen registers + * fmkg_ar: Action Register value + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_ar_wait(struct fman_kg_regs __iomem *regs, u32 fmkg_ar) +{ + iowrite32be(fmkg_ar, ®s->fmkg_ar); + + /* Wait for GO bit field to be idle */ + while (fmkg_ar & FM_KG_KGAR_GO) + fmkg_ar = ioread32be(®s->fmkg_ar); + + if (fmkg_ar & FM_KG_KGAR_ERR) + return -EINVAL; + + return 0; +} + +/* build_ar_scheme + * + * Build Action Register value for scheme settings + * + * scheme_id: Scheme ID + * update_counter: update scheme counter + * write: true for action to write the scheme or false for read action + * + * Return: AR value + */ +static u32 build_ar_scheme(u8 scheme_id, bool update_counter, bool write) +{ + u32 rw = (u32)(write ? FM_KG_KGAR_WRITE : FM_KG_KGAR_READ); + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_SCHEME_ENTRY | + DUMMY_PORT_ID | + ((u32)scheme_id << FM_KG_KGAR_NUM_SHIFT) | + (update_counter ? FM_KG_KGAR_SCM_WSEL_UPDATE_CNT : 0)); +} + +/* build_ar_bind_scheme + * + * Build Action Register value for port binding to schemes + * + * hwport_id: HW Port ID + * write: true for action to write the bind or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_scheme(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_SP); +} + +/* keygen_write_sp + * + * Write Scheme Partition Register with specified value + * + * regs: KeyGen Registers + * sp: Scheme Partition register value + * add: true to add a scheme partition or false to clear + * + * Return: none + */ +static void keygen_write_sp(struct fman_kg_regs __iomem *regs, u32 sp, bool add) +{ + u32 tmp; + + tmp = ioread32be(®s->fmkg_pe.fmkg_pe_sp); + + if (add) + tmp |= sp; + else + tmp &= ~sp; + + iowrite32be(tmp, ®s->fmkg_pe.fmkg_pe_sp); +} + +/* build_ar_bind_cls_plan + * + * Build Action Register value for Classification Plan + * + * hwport_id: HW Port ID + * write: true for action to write the CP or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_cls_plan(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_CPP); +} + +/* keygen_write_cpp + * + * Write Classification Plan Partition Register with specified value + * + * regs: KeyGen Registers + * cpp: CPP register value + * + * Return: none + */ +static void keygen_write_cpp(struct fman_kg_regs __iomem *regs, u32 cpp) +{ + iowrite32be(cpp, ®s->fmkg_pe.fmkg_pe_cpp); +} + +/* keygen_write_scheme + * + * Write all Schemes Registers with specified values + * + * regs: KeyGen Registers + * scheme_id: Scheme ID + * scheme_regs: Scheme registers values desired to be written + * update_counter: update scheme counter + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_scheme(struct fman_kg_regs __iomem *regs, u8 scheme_id, + struct fman_kg_scheme_regs *scheme_regs, + bool update_counter) +{ + u32 ar_reg; + int err, i; + + /* Write indirect scheme registers */ + iowrite32be(scheme_regs->kgse_mode, ®s->fmkg_sch.kgse_mode); + iowrite32be(scheme_regs->kgse_ekfc, ®s->fmkg_sch.kgse_ekfc); + iowrite32be(scheme_regs->kgse_ekdv, ®s->fmkg_sch.kgse_ekdv); + iowrite32be(scheme_regs->kgse_bmch, ®s->fmkg_sch.kgse_bmch); + iowrite32be(scheme_regs->kgse_bmcl, ®s->fmkg_sch.kgse_bmcl); + iowrite32be(scheme_regs->kgse_fqb, ®s->fmkg_sch.kgse_fqb); + iowrite32be(scheme_regs->kgse_hc, ®s->fmkg_sch.kgse_hc); + iowrite32be(scheme_regs->kgse_ppc, ®s->fmkg_sch.kgse_ppc); + iowrite32be(scheme_regs->kgse_spc, ®s->fmkg_sch.kgse_spc); + iowrite32be(scheme_regs->kgse_dv0, ®s->fmkg_sch.kgse_dv0); + iowrite32be(scheme_regs->kgse_dv1, ®s->fmkg_sch.kgse_dv1); + iowrite32be(scheme_regs->kgse_ccbs, ®s->fmkg_sch.kgse_ccbs); + iowrite32be(scheme_regs->kgse_mv, ®s->fmkg_sch.kgse_mv); + iowrite32be(scheme_regs->kgse_om, ®s->fmkg_sch.kgse_om); + iowrite32be(scheme_regs->kgse_vsp, ®s->fmkg_sch.kgse_vsp); + + for (i = 0 ; i < FM_KG_NUM_OF_GENERIC_REGS ; i++) + iowrite32be(scheme_regs->kgse_gec[i], + ®s->fmkg_sch.kgse_gec[i]); + + /* Write AR (Action register) */ + ar_reg = build_ar_scheme(scheme_id, update_counter, true); + err = keygen_write_ar_wait(regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return err; +} + +/* get_free_scheme_id + * + * Find the first free scheme available to be used + * + * keygen: KeyGen handle + * scheme_id: pointer to scheme id + * + * Return: 0 on success, -EINVAL when the are no available free schemes + */ +static int get_free_scheme_id(struct fman_keygen *keygen, u8 *scheme_id) +{ + u8 i; + + for (i = 0; i < FM_KG_MAX_NUM_OF_SCHEMES; i++) + if (!keygen->schemes[i].used) { + *scheme_id = i; + return 0; + } + + return -EINVAL; +} + +/* get_scheme + * + * Provides the scheme for specified ID + * + * keygen: KeyGen handle + * scheme_id: Scheme ID + * + * Return: handle to required scheme + */ +static struct keygen_scheme *get_scheme(struct fman_keygen *keygen, + u8 scheme_id) +{ + if (scheme_id >= FM_KG_MAX_NUM_OF_SCHEMES) + return NULL; + return &keygen->schemes[scheme_id]; +} + +/* keygen_bind_port_to_schemes + * + * Bind the port to schemes + * + * keygen: KeyGen handle + * scheme_id: id of the scheme to bind to + * bind: true to bind the port or false to unbind it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_bind_port_to_schemes(struct fman_keygen *keygen, + u8 scheme_id, + bool bind) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct keygen_scheme *scheme; + u32 ar_reg; + u32 schemes_vector = 0; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (!scheme->used) { + pr_err("Cannot bind port to an invalid scheme\n"); + return -EINVAL; + } + + schemes_vector |= 1 << (31 - scheme_id); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, false); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Reading Action Register failed\n"); + return err; + } + + keygen_write_sp(keygen_regs, schemes_vector, bind); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, true); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return 0; +} + +/* keygen_scheme_setup + * + * Setup the scheme according to required configuration + * + * keygen: KeyGen handle + * scheme_id: scheme ID + * enable: true to enable scheme or false to disable it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_scheme_setup(struct fman_keygen *keygen, u8 scheme_id, + bool enable) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct fman_kg_scheme_regs scheme_regs; + struct keygen_scheme *scheme; + u32 tmp_reg; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (enable && scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear scheme registers */ + memset(&scheme_regs, 0, sizeof(struct fman_kg_scheme_regs)); + + /* Setup all scheme registers: */ + tmp_reg = 0; + + if (enable) { + /* Enable Scheme */ + tmp_reg |= KG_SCH_MODE_EN; + /* Enqueue frame NIA */ + tmp_reg |= ENQUEUE_KG_DFLT_NIA; + } + + scheme_regs.kgse_mode = tmp_reg; + + scheme_regs.kgse_mv = scheme->match_vector; + + /* Scheme don't override StorageProfile: + * valid only for DPAA_VERSION >= 11 + */ + scheme_regs.kgse_vsp = KG_SCH_VSP_NO_KSP_EN; + + /* Configure Hard-Coded Rx Hashing: */ + + if (scheme->use_hashing) { + /* configure kgse_ekfc */ + scheme_regs.kgse_ekfc = DEFAULT_HASH_KEY_EXTRACT_FIELDS; + + /* configure kgse_ekdv */ + tmp_reg = 0; + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_0 << + KG_SCH_DEF_IP_ADDR_SHIFT); + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_1 << + KG_SCH_DEF_L4_PORT_SHIFT); + scheme_regs.kgse_ekdv = tmp_reg; + + /* configure kgse_dv0 */ + scheme_regs.kgse_dv0 = DEFAULT_HASH_KEY_IPv4_ADDR; + /* configure kgse_dv1 */ + scheme_regs.kgse_dv1 = DEFAULT_HASH_KEY_L4_PORT; + + /* configure kgse_hc */ + tmp_reg = 0; + tmp_reg |= ((scheme->hash_fqid_count - 1) << + DEFAULT_HASH_DIST_FQID_SHIFT); + tmp_reg |= scheme->hashShift << KG_SCH_HASH_CONFIG_SHIFT_SHIFT; + + if (scheme->symmetric_hash) { + /* Normally extraction key should be verified if + * complies with symmetric hash + * But because extraction is hard-coded, we are sure + * the key is symmetric + */ + tmp_reg |= KG_SCH_HASH_CONFIG_SYM; + } + scheme_regs.kgse_hc = tmp_reg; + } else { + scheme_regs.kgse_ekfc = 0; + scheme_regs.kgse_hc = 0; + scheme_regs.kgse_ekdv = 0; + scheme_regs.kgse_dv0 = 0; + scheme_regs.kgse_dv1 = 0; + } + + /* configure kgse_fqb: Scheme FQID base */ + tmp_reg = 0; + tmp_reg |= scheme->base_fqid; + scheme_regs.kgse_fqb = tmp_reg; + + /* features not used by hard-coded configuration */ + scheme_regs.kgse_bmch = 0; + scheme_regs.kgse_bmcl = 0; + scheme_regs.kgse_spc = 0; + + /* Write scheme registers */ + err = keygen_write_scheme(keygen_regs, scheme_id, &scheme_regs, true); + if (err != 0) { + pr_err("Writing scheme registers failed\n"); + return err; + } + + /* Update used field for Scheme */ + scheme->used = enable; + + return 0; +} + +/* keygen_init + * + * KeyGen initialization: + * Initializes and enables KeyGen, allocate driver memory, setup registers, + * clear port bindings, invalidate all schemes + * + * keygen_regs: KeyGen registers base address + * + * Return: Handle to KeyGen driver + */ +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs) +{ + struct fman_keygen *keygen; + u32 ar; + int i; + + /* Allocate memory for KeyGen driver */ + keygen = kzalloc(sizeof(*keygen), GFP_KERNEL); + if (!keygen) + return NULL; + + keygen->keygen_regs = keygen_regs; + + /* KeyGen initialization (for Master partition): + * Setup KeyGen registers + */ + iowrite32be(ENQUEUE_KG_DFLT_NIA, &keygen_regs->fmkg_gcr); + + iowrite32be(FM_EX_KG_DOUBLE_ECC | FM_EX_KG_KEYSIZE_OVERFLOW, + &keygen_regs->fmkg_eer); + + iowrite32be(0, &keygen_regs->fmkg_fdor); + iowrite32be(0, &keygen_regs->fmkg_gdv0r); + iowrite32be(0, &keygen_regs->fmkg_gdv1r); + + /* Clear binding between ports to schemes and classification plans + * so that all ports are not bound to any scheme/classification plan + */ + for (i = 0; i < FMAN_MAX_NUM_OF_HW_PORTS; i++) { + /* Clear all pe sp schemes registers */ + keygen_write_sp(keygen_regs, 0xffffffff, false); + ar = build_ar_bind_scheme(i, true); + keygen_write_ar_wait(keygen_regs, ar); + + /* Clear all pe cpp classification plans registers */ + keygen_write_cpp(keygen_regs, 0); + ar = build_ar_bind_cls_plan(i, true); + keygen_write_ar_wait(keygen_regs, ar); + } + + /* Enable all scheme interrupts */ + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seer); + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seeer); + + /* Enable KyeGen */ + iowrite32be(ioread32be(&keygen_regs->fmkg_gcr) | FM_KG_KGGCR_EN, + &keygen_regs->fmkg_gcr); + + return keygen; +} +EXPORT_SYMBOL(keygen_init); + +/* keygen_port_hashing_init + * + * Initializes a port for Rx Hashing with specified configuration parameters + * + * keygen: KeyGen handle + * hw_port_id: HW Port ID + * hash_base_fqid: Hashing Base FQID used for spreading + * hash_size: Hashing size + * + * Return: Zero for success or error code in case of failure + */ +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size) +{ + struct keygen_scheme *scheme; + u8 scheme_id; + int err; + + /* Validate Scheme configuration parameters */ + if (hash_base_fqid == 0 || (hash_base_fqid & ~0x00FFFFFF)) { + pr_err("Base FQID must be between 1 and 2^24-1\n"); + return -EINVAL; + } + if (hash_size == 0 || (hash_size & (hash_size - 1)) != 0) { + pr_err("Hash size must be power of two\n"); + return -EINVAL; + } + + /* Find a free scheme */ + err = get_free_scheme_id(keygen, &scheme_id); + if (err) { + pr_err("The maximum number of available Schemes has been exceeded\n"); + return -EINVAL; + } + + /* Create and configure Hard-Coded Scheme: */ + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear all scheme fields because the scheme may have been + * previously used + */ + memset(scheme, 0, sizeof(struct keygen_scheme)); + + /* Setup scheme: */ + scheme->hw_port_id = hw_port_id; + scheme->use_hashing = true; + scheme->base_fqid = hash_base_fqid; + scheme->hash_fqid_count = hash_size; + scheme->symmetric_hash = DEFAULT_SYMMETRIC_HASH; + scheme->hashShift = DEFAULT_HASH_SHIFT; + + /* All Schemes in hard-coded configuration + * are Indirect Schemes + */ + scheme->match_vector = 0; + + err = keygen_scheme_setup(keygen, scheme_id, true); + if (err != 0) { + pr_err("Scheme setup failed\n"); + return err; + } + + /* Bind Rx port to Scheme */ + err = keygen_bind_port_to_schemes(keygen, scheme_id, true); + if (err != 0) { + pr_err("Binding port to schemes failed\n"); + return err; + } + + return 0; +} +EXPORT_SYMBOL(keygen_port_hashing_init); diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.h b/drivers/net/ethernet/freescale/fman/fman_keygen.h new file mode 100644 index 0000000..c4640de --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __KEYGEN_H +#define __KEYGEN_H + +#include <linux/io.h> + +struct fman_keygen; +struct fman_kg_regs; + +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs); + +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size); + +#endif /* __KEYGEN_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index d51e6aa..f571b3c 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -44,6 +44,7 @@ #include "fman.h" #include "fman_port.h" #include "fman_sp.h" +#include "fman_keygen.h" /* Queue ID */ #define DFLT_FQ_ID 0x00FFFFFF @@ -184,6 +185,7 @@ #define NIA_ENG_QMI_ENQ 0x00540000 #define NIA_ENG_QMI_DEQ 0x00580000 #define NIA_ENG_HWP 0x00440000 +#define NIA_ENG_HWK 0x00480000 #define NIA_BMI_AC_ENQ_FRAME 0x00000002 #define NIA_BMI_AC_TX_RELEASE 0x000002C0 #define NIA_BMI_AC_RELEASE 0x000000C0 @@ -394,6 +396,8 @@ struct fman_port_bpools { struct fman_port_cfg { u32 dflt_fqid; u32 err_fqid; + u32 pcd_base_fqid; + u32 pcd_fqs_count; u8 deq_sp; bool deq_high_priority; enum fman_port_deq_type deq_type; @@ -1271,6 +1275,10 @@ static void set_rx_dflt_cfg(struct fman_port *port, port_params->specific_params.rx_params.err_fqid; port->cfg->dflt_fqid = port_params->specific_params.rx_params.dflt_fqid; + port->cfg->pcd_base_fqid = + port_params->specific_params.rx_params.pcd_base_fqid; + port->cfg->pcd_fqs_count = + port_params->specific_params.rx_params.pcd_fqs_count; } static void set_tx_dflt_cfg(struct fman_port *port, @@ -1398,6 +1406,24 @@ int fman_port_config(struct fman_port *port, struct fman_port_params *params) EXPORT_SYMBOL(fman_port_config); /** + * fman_port_use_kg_hash + * port: A pointer to a FM Port module. + * Sets the HW KeyGen or the BMI as HW Parser next engine, enabling + * or bypassing the KeyGen hashing of Rx traffic + */ +void fman_port_use_kg_hash(struct fman_port *port, bool enable) +{ + if (enable) + /* After the Parser frames go to KeyGen */ + iowrite32be(NIA_ENG_HWK, &port->bmi_regs->rx.fmbm_rfpne); + else + /* After the Parser frames go to BMI */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME, + &port->bmi_regs->rx.fmbm_rfpne); +} +EXPORT_SYMBOL(fman_port_use_kg_hash); + +/** * fman_port_init * port: A pointer to a FM Port module. * Initializes the FM PORT module by defining the software structure and @@ -1407,9 +1433,10 @@ EXPORT_SYMBOL(fman_port_config); */ int fman_port_init(struct fman_port *port) { + struct fman_port_init_params params; + struct fman_keygen *keygen; struct fman_port_cfg *cfg; int err; - struct fman_port_init_params params; if (is_init_done(port->cfg)) return -EINVAL; @@ -1472,6 +1499,17 @@ int fman_port_init(struct fman_port *port) if (err) return err; + if (port->cfg->pcd_fqs_count) { + keygen = port->dts_params.fman->keygen; + err = keygen_port_hashing_init(keygen, port->port_id, + port->cfg->pcd_base_fqid, + port->cfg->pcd_fqs_count); + if (err) + return err; + + fman_port_use_kg_hash(port, true); + } + kfree(port->cfg); port->cfg = NULL; diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h index 8ba9017..5a99611 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.h +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -100,6 +100,9 @@ struct fman_port; struct fman_port_rx_params { u32 err_fqid; /* Error Queue Id. */ u32 dflt_fqid; /* Default Queue Id. */ + u32 pcd_base_fqid; /* PCD base Queue Id. */ + u32 pcd_fqs_count; /* Number of PCD FQs. */ + /* Which external buffer pools are used * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes. */ @@ -134,6 +137,8 @@ struct fman_port_params { int fman_port_config(struct fman_port *port, struct fman_port_params *params); +void fman_port_use_kg_hash(struct fman_port *port, bool enable); + int fman_port_init(struct fman_port *port); int fman_port_cfg_buf_prefix_content(struct fman_port *port, -- 2.1.0