// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 
 | 
/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 
 | 
  
 | 
#include <linux/kernel.h> 
 | 
#include <linux/slab.h> 
 | 
  
 | 
#include "reg.h" 
 | 
#include "core.h" 
 | 
#include "spectrum.h" 
 | 
#include "spectrum_acl_tcam.h" 
 | 
  
 | 
struct mlxsw_sp1_acl_tcam_region { 
 | 
    struct mlxsw_sp_acl_ctcam_region cregion; 
 | 
    struct mlxsw_sp_acl_tcam_region *region; 
 | 
    struct { 
 | 
        struct mlxsw_sp_acl_ctcam_chunk cchunk; 
 | 
        struct mlxsw_sp_acl_ctcam_entry centry; 
 | 
        struct mlxsw_sp_acl_rule_info *rulei; 
 | 
    } catchall; 
 | 
}; 
 | 
  
 | 
struct mlxsw_sp1_acl_tcam_chunk { 
 | 
    struct mlxsw_sp_acl_ctcam_chunk cchunk; 
 | 
}; 
 | 
  
 | 
struct mlxsw_sp1_acl_tcam_entry { 
 | 
    struct mlxsw_sp_acl_ctcam_entry centry; 
 | 
}; 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregion, 
 | 
                    struct mlxsw_sp_acl_ctcam_entry *centry, 
 | 
                    const char *mask) 
 | 
{ 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static void 
 | 
mlxsw_sp1_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region *cregion, 
 | 
                    struct mlxsw_sp_acl_ctcam_entry *centry) 
 | 
{ 
 | 
} 
 | 
  
 | 
static const struct mlxsw_sp_acl_ctcam_region_ops 
 | 
mlxsw_sp1_acl_ctcam_region_ops = { 
 | 
    .entry_insert = mlxsw_sp1_acl_ctcam_region_entry_insert, 
 | 
    .entry_remove = mlxsw_sp1_acl_ctcam_region_entry_remove, 
 | 
}; 
 | 
  
 | 
static int mlxsw_sp1_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv, 
 | 
                   struct mlxsw_sp_acl_tcam *tcam) 
 | 
{ 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static void mlxsw_sp1_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv) 
 | 
{ 
 | 
} 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_ctcam_region_catchall_add(struct mlxsw_sp *mlxsw_sp, 
 | 
                    struct mlxsw_sp1_acl_tcam_region *region) 
 | 
{ 
 | 
    struct mlxsw_sp_acl_rule_info *rulei; 
 | 
    int err; 
 | 
  
 | 
    mlxsw_sp_acl_ctcam_chunk_init(®ion->cregion, 
 | 
                      ®ion->catchall.cchunk, 
 | 
                      MLXSW_SP_ACL_TCAM_CATCHALL_PRIO); 
 | 
    rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl, NULL); 
 | 
    if (IS_ERR(rulei)) { 
 | 
        err = PTR_ERR(rulei); 
 | 
        goto err_rulei_create; 
 | 
    } 
 | 
    err = mlxsw_sp_acl_rulei_act_continue(rulei); 
 | 
    if (WARN_ON(err)) 
 | 
        goto err_rulei_act_continue; 
 | 
    err = mlxsw_sp_acl_rulei_commit(rulei); 
 | 
    if (err) 
 | 
        goto err_rulei_commit; 
 | 
    err = mlxsw_sp_acl_ctcam_entry_add(mlxsw_sp, ®ion->cregion, 
 | 
                       ®ion->catchall.cchunk, 
 | 
                       ®ion->catchall.centry, 
 | 
                       rulei, false); 
 | 
    if (err) 
 | 
        goto err_entry_add; 
 | 
    region->catchall.rulei = rulei; 
 | 
    return 0; 
 | 
  
 | 
err_entry_add: 
 | 
err_rulei_commit: 
 | 
err_rulei_act_continue: 
 | 
    mlxsw_sp_acl_rulei_destroy(rulei); 
 | 
err_rulei_create: 
 | 
    mlxsw_sp_acl_ctcam_chunk_fini(®ion->catchall.cchunk); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static void 
 | 
mlxsw_sp1_acl_ctcam_region_catchall_del(struct mlxsw_sp *mlxsw_sp, 
 | 
                    struct mlxsw_sp1_acl_tcam_region *region) 
 | 
{ 
 | 
    struct mlxsw_sp_acl_rule_info *rulei = region->catchall.rulei; 
 | 
  
 | 
    mlxsw_sp_acl_ctcam_entry_del(mlxsw_sp, ®ion->cregion, 
 | 
                     ®ion->catchall.cchunk, 
 | 
                     ®ion->catchall.centry); 
 | 
    mlxsw_sp_acl_rulei_destroy(rulei); 
 | 
    mlxsw_sp_acl_ctcam_chunk_fini(®ion->catchall.cchunk); 
 | 
} 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv, 
 | 
                   void *tcam_priv, 
 | 
                   struct mlxsw_sp_acl_tcam_region *_region, 
 | 
                   void *hints_priv) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_region *region = region_priv; 
 | 
    int err; 
 | 
  
 | 
    err = mlxsw_sp_acl_ctcam_region_init(mlxsw_sp, ®ion->cregion, 
 | 
                         _region, 
 | 
                         &mlxsw_sp1_acl_ctcam_region_ops); 
 | 
    if (err) 
 | 
        return err; 
 | 
    err = mlxsw_sp1_acl_ctcam_region_catchall_add(mlxsw_sp, region); 
 | 
    if (err) 
 | 
        goto err_catchall_add; 
 | 
    region->region = _region; 
 | 
    return 0; 
 | 
  
 | 
err_catchall_add: 
 | 
    mlxsw_sp_acl_ctcam_region_fini(®ion->cregion); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static void 
 | 
mlxsw_sp1_acl_tcam_region_fini(struct mlxsw_sp *mlxsw_sp, void *region_priv) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_region *region = region_priv; 
 | 
  
 | 
    mlxsw_sp1_acl_ctcam_region_catchall_del(mlxsw_sp, region); 
 | 
    mlxsw_sp_acl_ctcam_region_fini(®ion->cregion); 
 | 
} 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_tcam_region_associate(struct mlxsw_sp *mlxsw_sp, 
 | 
                    struct mlxsw_sp_acl_tcam_region *region) 
 | 
{ 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static void mlxsw_sp1_acl_tcam_chunk_init(void *region_priv, void *chunk_priv, 
 | 
                      unsigned int priority) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_region *region = region_priv; 
 | 
    struct mlxsw_sp1_acl_tcam_chunk *chunk = chunk_priv; 
 | 
  
 | 
    mlxsw_sp_acl_ctcam_chunk_init(®ion->cregion, &chunk->cchunk, 
 | 
                      priority); 
 | 
} 
 | 
  
 | 
static void mlxsw_sp1_acl_tcam_chunk_fini(void *chunk_priv) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_chunk *chunk = chunk_priv; 
 | 
  
 | 
    mlxsw_sp_acl_ctcam_chunk_fini(&chunk->cchunk); 
 | 
} 
 | 
  
 | 
static int mlxsw_sp1_acl_tcam_entry_add(struct mlxsw_sp *mlxsw_sp, 
 | 
                    void *region_priv, void *chunk_priv, 
 | 
                    void *entry_priv, 
 | 
                    struct mlxsw_sp_acl_rule_info *rulei) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_region *region = region_priv; 
 | 
    struct mlxsw_sp1_acl_tcam_chunk *chunk = chunk_priv; 
 | 
    struct mlxsw_sp1_acl_tcam_entry *entry = entry_priv; 
 | 
  
 | 
    return mlxsw_sp_acl_ctcam_entry_add(mlxsw_sp, ®ion->cregion, 
 | 
                        &chunk->cchunk, &entry->centry, 
 | 
                        rulei, false); 
 | 
} 
 | 
  
 | 
static void mlxsw_sp1_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp, 
 | 
                     void *region_priv, void *chunk_priv, 
 | 
                     void *entry_priv) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_region *region = region_priv; 
 | 
    struct mlxsw_sp1_acl_tcam_chunk *chunk = chunk_priv; 
 | 
    struct mlxsw_sp1_acl_tcam_entry *entry = entry_priv; 
 | 
  
 | 
    mlxsw_sp_acl_ctcam_entry_del(mlxsw_sp, ®ion->cregion, 
 | 
                     &chunk->cchunk, &entry->centry); 
 | 
} 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp, 
 | 
                    void *region_priv, void *entry_priv, 
 | 
                    struct mlxsw_sp_acl_rule_info *rulei) 
 | 
{ 
 | 
    return -EOPNOTSUPP; 
 | 
} 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_tcam_region_entry_activity_get(struct mlxsw_sp *mlxsw_sp, 
 | 
                         struct mlxsw_sp_acl_tcam_region *_region, 
 | 
                         unsigned int offset, 
 | 
                         bool *activity) 
 | 
{ 
 | 
    char ptce2_pl[MLXSW_REG_PTCE2_LEN]; 
 | 
    int err; 
 | 
  
 | 
    mlxsw_reg_ptce2_pack(ptce2_pl, true, MLXSW_REG_PTCE2_OP_QUERY_CLEAR_ON_READ, 
 | 
                 _region->tcam_region_info, offset, 0); 
 | 
    err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl); 
 | 
    if (err) 
 | 
        return err; 
 | 
    *activity = mlxsw_reg_ptce2_a_get(ptce2_pl); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int 
 | 
mlxsw_sp1_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp, 
 | 
                      void *region_priv, void *entry_priv, 
 | 
                      bool *activity) 
 | 
{ 
 | 
    struct mlxsw_sp1_acl_tcam_region *region = region_priv; 
 | 
    struct mlxsw_sp1_acl_tcam_entry *entry = entry_priv; 
 | 
    unsigned int offset; 
 | 
  
 | 
    offset = mlxsw_sp_acl_ctcam_entry_offset(&entry->centry); 
 | 
    return mlxsw_sp1_acl_tcam_region_entry_activity_get(mlxsw_sp, 
 | 
                                region->region, 
 | 
                                offset, activity); 
 | 
} 
 | 
  
 | 
const struct mlxsw_sp_acl_tcam_ops mlxsw_sp1_acl_tcam_ops = { 
 | 
    .key_type        = MLXSW_REG_PTAR_KEY_TYPE_FLEX, 
 | 
    .priv_size        = 0, 
 | 
    .init            = mlxsw_sp1_acl_tcam_init, 
 | 
    .fini            = mlxsw_sp1_acl_tcam_fini, 
 | 
    .region_priv_size    = sizeof(struct mlxsw_sp1_acl_tcam_region), 
 | 
    .region_init        = mlxsw_sp1_acl_tcam_region_init, 
 | 
    .region_fini        = mlxsw_sp1_acl_tcam_region_fini, 
 | 
    .region_associate    = mlxsw_sp1_acl_tcam_region_associate, 
 | 
    .chunk_priv_size    = sizeof(struct mlxsw_sp1_acl_tcam_chunk), 
 | 
    .chunk_init        = mlxsw_sp1_acl_tcam_chunk_init, 
 | 
    .chunk_fini        = mlxsw_sp1_acl_tcam_chunk_fini, 
 | 
    .entry_priv_size    = sizeof(struct mlxsw_sp1_acl_tcam_entry), 
 | 
    .entry_add        = mlxsw_sp1_acl_tcam_entry_add, 
 | 
    .entry_del        = mlxsw_sp1_acl_tcam_entry_del, 
 | 
    .entry_action_replace    = mlxsw_sp1_acl_tcam_entry_action_replace, 
 | 
    .entry_activity_get    = mlxsw_sp1_acl_tcam_entry_activity_get, 
 | 
}; 
 |