#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include "fman_keygen.h"
#define FMAN_MAX_NUM_OF_HW_PORTS 64
#define FM_KG_MAX_NUM_OF_SCHEMES 32
#define FM_KG_NUM_OF_GENERIC_REGS 8
#define DUMMY_PORT_ID 0
#define KG_SCH_DEF_USE_KGSE_DV_0 2
#define KG_SCH_DEF_USE_KGSE_DV_1 3
#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
#define FM_KG_KGGCR_EN 0x80000000
#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
#define FM_EX_KG_DOUBLE_ECC 0x80000000
#define FM_EX_KG_KEYSIZE_OVERFLOW 0x40000000
#define KG_SCH_MODE_EN 0x80000000
#define KG_SCH_VSP_NO_KSP_EN 0x80000000
#define KG_SCH_HASH_CONFIG_SYM 0x40000000
#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
#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)
#define DEFAULT_HASH_DIST_FQID_SHIFT 0
#define DEFAULT_HASH_SHIFT 0
#define DEFAULT_SYMMETRIC_HASH false
#define DEFAULT_HASH_KEY_EXTRACT_FIELDS \
(KG_SCH_KN_IPSRC1 | KG_SCH_KN_IPDST1 | \
KG_SCH_KN_L4PSRC | KG_SCH_KN_L4PDST | \
KG_SCH_KN_IPSEC_SPI)
#define DEFAULT_HASH_KEY_IPv4_ADDR 0x0A0A0A0A
#define DEFAULT_HASH_KEY_L4_PORT 0x0B0B0B0B
struct fman_kg_scheme_regs {
u32 kgse_mode;
u32 kgse_ekfc;
u32 kgse_ekdv;
u32 kgse_bmch;
u32 kgse_bmcl;
u32 kgse_fqb;
u32 kgse_hc;
u32 kgse_ppc;
u32 kgse_gec[FM_KG_NUM_OF_GENERIC_REGS];
u32 kgse_spc;
u32 kgse_dv0;
u32 kgse_dv1;
u32 kgse_ccbs;
u32 kgse_mv;
u32 kgse_om;
u32 kgse_vsp;
};
struct fman_kg_pe_regs {
u32 fmkg_pe_sp;
u32 fmkg_pe_cpp;
};
struct fman_kg_regs {
u32 fmkg_gcr;
u32 res004;
u32 res008;
u32 fmkg_eer;
u32 fmkg_eeer;
u32 res014;
u32 res018;
u32 fmkg_seer;
u32 fmkg_seeer;
u32 fmkg_gsr;
u32 fmkg_tpc;
u32 fmkg_serc;
u32 res030[4];
u32 fmkg_fdor;
u32 fmkg_gdv0r;
u32 fmkg_gdv1r;
u32 res04c[6];
u32 fmkg_feer;
u32 res068[38];
union {
u32 fmkg_indirect[63];
struct fman_kg_scheme_regs fmkg_sch;
struct fman_kg_pe_regs fmkg_pe;
};
u32 fmkg_ar;
};
struct keygen_scheme {
bool used;
u8 hw_port_id;
u32 base_fqid;
u32 hash_fqid_count;
bool use_hashing;
bool symmetric_hash;
u8 hashShift;
u32 match_vector;
};
struct fman_keygen {
struct keygen_scheme schemes[FM_KG_MAX_NUM_OF_SCHEMES];
struct fman_kg_regs __iomem *keygen_regs;
};
static int keygen_write_ar_wait(struct fman_kg_regs __iomem *regs, u32 fmkg_ar)
{
iowrite32be(fmkg_ar, ®s->fmkg_ar);
while (fmkg_ar & FM_KG_KGAR_GO)
fmkg_ar = ioread32be(®s->fmkg_ar);
if (fmkg_ar & FM_KG_KGAR_ERR)
return -EINVAL;
return 0;
}
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));
}
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);
}
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);
}
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);
}
static void keygen_write_cpp(struct fman_kg_regs __iomem *regs, u32 cpp)
{
iowrite32be(cpp, ®s->fmkg_pe.fmkg_pe_cpp);
}
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;
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]);
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;
}
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;
}
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];
}
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;
}
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;
}
memset(&scheme_regs, 0, sizeof(struct fman_kg_scheme_regs));
tmp_reg = 0;
if (enable) {
tmp_reg |= KG_SCH_MODE_EN;
tmp_reg |= ENQUEUE_KG_DFLT_NIA;
}
scheme_regs.kgse_mode = tmp_reg;
scheme_regs.kgse_mv = scheme->match_vector;
scheme_regs.kgse_vsp = KG_SCH_VSP_NO_KSP_EN;
if (scheme->use_hashing) {
scheme_regs.kgse_ekfc = DEFAULT_HASH_KEY_EXTRACT_FIELDS;
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;
scheme_regs.kgse_dv0 = DEFAULT_HASH_KEY_IPv4_ADDR;
scheme_regs.kgse_dv1 = DEFAULT_HASH_KEY_L4_PORT;
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) {
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;
}
tmp_reg = 0;
tmp_reg |= scheme->base_fqid;
scheme_regs.kgse_fqb = tmp_reg;
scheme_regs.kgse_bmch = 0;
scheme_regs.kgse_bmcl = 0;
scheme_regs.kgse_spc = 0;
err = keygen_write_scheme(keygen_regs, scheme_id, &scheme_regs, true);
if (err != 0) {
pr_err("Writing scheme registers failed\n");
return err;
}
scheme->used = enable;
return 0;
}
struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs)
{
struct fman_keygen *keygen;
u32 ar;
int i;
keygen = kzalloc(sizeof(*keygen), GFP_KERNEL);
if (!keygen)
return NULL;
keygen->keygen_regs = keygen_regs;
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);
for (i = 0; i < FMAN_MAX_NUM_OF_HW_PORTS; i++) {
keygen_write_sp(keygen_regs, 0xffffffff, false);
ar = build_ar_bind_scheme(i, true);
keygen_write_ar_wait(keygen_regs, ar);
keygen_write_cpp(keygen_regs, 0);
ar = build_ar_bind_cls_plan(i, true);
keygen_write_ar_wait(keygen_regs, ar);
}
iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seer);
iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seeer);
iowrite32be(ioread32be(&keygen_regs->fmkg_gcr) | FM_KG_KGGCR_EN,
&keygen_regs->fmkg_gcr);
return keygen;
}
EXPORT_SYMBOL(keygen_init);
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;
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;
}
err = get_free_scheme_id(keygen, &scheme_id);
if (err) {
pr_err("The maximum number of available Schemes has been exceeded\n");
return -EINVAL;
}
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;
}
memset(scheme, 0, sizeof(struct keygen_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;
scheme->match_vector = 0;
err = keygen_scheme_setup(keygen, scheme_id, true);
if (err != 0) {
pr_err("Scheme setup failed\n");
return err;
}
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