unsigned int                    num_stats;
        int                             num_ports;
        int                             num_tx_queues;
-       const struct vcap_props         *vcap;
+       struct vcap_props               *vcap;
        int                             switch_pci_bar;
        int                             imdio_pci_bar;
        const struct ptp_clock_info     *ptp_caps;
 
        REG(VCAP_CACHE_ACTION_DAT,              0x000208),
        REG(VCAP_CACHE_CNT_DAT,                 0x000308),
        REG(VCAP_CACHE_TG_DAT,                  0x000388),
+       /* VCAP_CONST */
+       REG(VCAP_CONST_VCAP_VER,                0x000398),
+       REG(VCAP_CONST_ENTRY_WIDTH,             0x00039c),
+       REG(VCAP_CONST_ENTRY_CNT,               0x0003a0),
+       REG(VCAP_CONST_ENTRY_SWCNT,             0x0003a4),
+       REG(VCAP_CONST_ENTRY_TG_WIDTH,          0x0003a8),
+       REG(VCAP_CONST_ACTION_DEF_CNT,          0x0003ac),
+       REG(VCAP_CONST_ACTION_WIDTH,            0x0003b0),
+       REG(VCAP_CONST_CNT_WIDTH,               0x0003b4),
+       REG(VCAP_CONST_CORE_CNT,                0x0003b8),
+       REG(VCAP_CONST_IF_CNT,                  0x0003bc),
 };
 
 static const u32 vsc9959_qsys_regmap[] = {
        [VCAP_IS2_ACT_HIT_CNT]                  = { 49, 32},
 };
 
-static const struct vcap_props vsc9959_vcap_props[] = {
+static struct vcap_props vsc9959_vcap_props[] = {
        [VCAP_ES0] = {
                .action_type_width = 0,
                .action_table = {
 
        REG(VCAP_CACHE_ACTION_DAT,              0x000208),
        REG(VCAP_CACHE_CNT_DAT,                 0x000308),
        REG(VCAP_CACHE_TG_DAT,                  0x000388),
+       /* VCAP_CONST */
+       REG(VCAP_CONST_VCAP_VER,                0x000398),
+       REG(VCAP_CONST_ENTRY_WIDTH,             0x00039c),
+       REG(VCAP_CONST_ENTRY_CNT,               0x0003a0),
+       REG(VCAP_CONST_ENTRY_SWCNT,             0x0003a4),
+       REG(VCAP_CONST_ENTRY_TG_WIDTH,          0x0003a8),
+       REG(VCAP_CONST_ACTION_DEF_CNT,          0x0003ac),
+       REG(VCAP_CONST_ACTION_WIDTH,            0x0003b0),
+       REG(VCAP_CONST_CNT_WIDTH,               0x0003b4),
+       REG_RESERVED(VCAP_CONST_CORE_CNT),
+       REG_RESERVED(VCAP_CONST_IF_CNT),
 };
 
 static const u32 vsc9953_qsys_regmap[] = {
        [VCAP_IS2_ACT_HIT_CNT]                  = { 50, 32},
 };
 
-static const struct vcap_props vsc9953_vcap_props[] = {
+static struct vcap_props vsc9953_vcap_props[] = {
        [VCAP_ES0] = {
                .action_type_width = 0,
                .action_table = {
 
  * Copyright (c) 2017 Microsemi Corporation
  */
 #include <linux/if_bridge.h>
+#include <soc/mscc/ocelot_vcap.h>
 #include "ocelot.h"
 #include "ocelot_vcap.h"
 
 
                 VCAP_SEL_ACTION | VCAP_SEL_COUNTER);
 }
 
+static void ocelot_vcap_detect_constants(struct ocelot *ocelot,
+                                        struct vcap_props *vcap)
+{
+       int counter_memory_width;
+       int num_default_actions;
+       int version;
+
+       version = ocelot_target_read(ocelot, vcap->target,
+                                    VCAP_CONST_VCAP_VER);
+       /* Only version 0 VCAP supported for now */
+       if (WARN_ON(version != 0))
+               return;
+
+       /* Width in bits of type-group field */
+       vcap->tg_width = ocelot_target_read(ocelot, vcap->target,
+                                           VCAP_CONST_ENTRY_TG_WIDTH);
+       /* Number of subwords per TCAM row */
+       vcap->sw_count = ocelot_target_read(ocelot, vcap->target,
+                                           VCAP_CONST_ENTRY_SWCNT);
+       /* Number of rows in TCAM. There can be this many full keys, or double
+        * this number half keys, or 4 times this number quarter keys.
+        */
+       vcap->entry_count = ocelot_target_read(ocelot, vcap->target,
+                                              VCAP_CONST_ENTRY_CNT);
+       /* Assuming there are 4 subwords per TCAM row, their layout in the
+        * actual TCAM (not in the cache) would be:
+        *
+        * |  SW 3  | TG 3 |  SW 2  | TG 2 |  SW 1  | TG 1 |  SW 0  | TG 0 |
+        *
+        * (where SW=subword and TG=Type-Group).
+        *
+        * What VCAP_CONST_ENTRY_CNT is giving us is the width of one full TCAM
+        * row. But when software accesses the TCAM through the cache
+        * registers, the Type-Group values are written through another set of
+        * registers VCAP_TG_DAT, and therefore, it appears as though the 4
+        * subwords are contiguous in the cache memory.
+        * Important mention: regardless of the number of key entries per row
+        * (and therefore of key size: 1 full key or 2 half keys or 4 quarter
+        * keys), software always has to configure 4 Type-Group values. For
+        * example, in the case of 1 full key, the driver needs to set all 4
+        * Type-Group to be full key.
+        *
+        * For this reason, we need to fix up the value that the hardware is
+        * giving us. We don't actually care about the width of the entry in
+        * the TCAM. What we care about is the width of the entry in the cache
+        * registers, which is how we get to interact with it. And since the
+        * VCAP_ENTRY_DAT cache registers access only the subwords and not the
+        * Type-Groups, this means we need to subtract the width of the
+        * Type-Groups when packing and unpacking key entry data in a TCAM row.
+        */
+       vcap->entry_width = ocelot_target_read(ocelot, vcap->target,
+                                              VCAP_CONST_ENTRY_WIDTH);
+       vcap->entry_width -= vcap->tg_width * vcap->sw_count;
+       num_default_actions = ocelot_target_read(ocelot, vcap->target,
+                                                VCAP_CONST_ACTION_DEF_CNT);
+       vcap->action_count = vcap->entry_count + num_default_actions;
+       vcap->action_width = ocelot_target_read(ocelot, vcap->target,
+                                               VCAP_CONST_ACTION_WIDTH);
+       /* The width of the counter memory, this is the complete width of all
+        * counter-fields associated with one full-word entry. There is one
+        * counter per entry sub-word (see CAP_CORE::ENTRY_SWCNT for number of
+        * subwords.)
+        */
+       vcap->counter_words = vcap->sw_count;
+       counter_memory_width = ocelot_target_read(ocelot, vcap->target,
+                                                 VCAP_CONST_CNT_WIDTH);
+       vcap->counter_width = counter_memory_width / vcap->counter_words;
+}
+
 int ocelot_vcap_init(struct ocelot *ocelot)
 {
        struct ocelot_vcap_block *block = &ocelot->block;
-
-       ocelot_vcap_init_one(ocelot, &ocelot->vcap[VCAP_IS2]);
+       int i;
 
        /* Create a policer that will drop the frames for the cpu.
         * This policer will be used as action in the acl rules to drop
        ocelot_write_gix(ocelot, 0x3fffff, ANA_POL_CIR_STATE,
                         OCELOT_POLICER_DISCARD);
 
+       for (i = 0; i < OCELOT_NUM_VCAP_BLOCKS; i++) {
+               struct vcap_props *vcap = &ocelot->vcap[i];
+
+               ocelot_vcap_detect_constants(ocelot, vcap);
+               ocelot_vcap_init_one(ocelot, vcap);
+       }
+
        block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
 
        INIT_LIST_HEAD(&block->rules);
 
 int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
                                    struct ocelot_vcap_filter *rule);
 
+void ocelot_detect_vcap_constants(struct ocelot *ocelot);
 int ocelot_vcap_init(struct ocelot *ocelot);
 
 int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
 
        REG(VCAP_CACHE_ACTION_DAT,                      0x000208),
        REG(VCAP_CACHE_CNT_DAT,                         0x000308),
        REG(VCAP_CACHE_TG_DAT,                          0x000388),
+       /* VCAP_CONST */
+       REG(VCAP_CONST_VCAP_VER,                        0x000398),
+       REG(VCAP_CONST_ENTRY_WIDTH,                     0x00039c),
+       REG(VCAP_CONST_ENTRY_CNT,                       0x0003a0),
+       REG(VCAP_CONST_ENTRY_SWCNT,                     0x0003a4),
+       REG(VCAP_CONST_ENTRY_TG_WIDTH,                  0x0003a8),
+       REG(VCAP_CONST_ACTION_DEF_CNT,                  0x0003ac),
+       REG(VCAP_CONST_ACTION_WIDTH,                    0x0003b0),
+       REG(VCAP_CONST_CNT_WIDTH,                       0x0003b4),
+       REG(VCAP_CONST_CORE_CNT,                        0x0003b8),
+       REG(VCAP_CONST_IF_CNT,                          0x0003bc),
 };
 
 static const u32 ocelot_ptp_regmap[] = {
        [VCAP_IS2_ACT_HIT_CNT]                  = { 49, 32},
 };
 
-static const struct vcap_props vsc7514_vcap_props[] = {
+static struct vcap_props vsc7514_vcap_props[] = {
        [VCAP_ES0] = {
                .action_type_width = 0,
                .action_table = {
 
        VCAP_CACHE_ACTION_DAT,
        VCAP_CACHE_CNT_DAT,
        VCAP_CACHE_TG_DAT,
+       /* VCAP_CONST */
+       VCAP_CONST_VCAP_VER,
+       VCAP_CONST_ENTRY_WIDTH,
+       VCAP_CONST_ENTRY_CNT,
+       VCAP_CONST_ENTRY_SWCNT,
+       VCAP_CONST_ENTRY_TG_WIDTH,
+       VCAP_CONST_ACTION_DEF_CNT,
+       VCAP_CONST_ACTION_WIDTH,
+       VCAP_CONST_CNT_WIDTH,
+       VCAP_CONST_CORE_CNT,
+       VCAP_CONST_IF_CNT,
 };
 
 enum ocelot_ptp_pins {
        struct list_head                multicast;
 
        struct ocelot_vcap_block        block;
-       const struct vcap_props         *vcap;
+       struct vcap_props               *vcap;
 
        /* Workqueue to check statistics for overflow with its lock */
        struct mutex                    stats_lock;
 
        VCAP_ES0,
        VCAP_IS1,
        VCAP_IS2,
+       __VCAP_COUNT,
 };
 
+#define OCELOT_NUM_VCAP_BLOCKS         __VCAP_COUNT
+
 struct vcap_props {
        u16 tg_width; /* Type-group width (in bits) */
        u16 sw_count; /* Sub word count */