openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

falcon_sim.c (15978B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  *
      8  * This software simulator can emulate basic register access for the
      9  * TSC/Eagle SerDes PHY.
     10  *
     11  * The simulator suppor both IEEE clause 22/45 access and Broadcom
     12  * proprietary SBUS access.
     13  *
     14  * Clause 22 address format:
     15  *   Bits [4:0]   : Clause 22 register address
     16  *   Bits [31:5]  : Unused
     17  *
     18  * Clause 45 address format:
     19  *   Bits [15:0]  : Clause 45 register address
     20  *   Bits [20:16] : Clause 45 DEVAD
     21  *   Bits [23:21] : Clause 45 indicator (001b)
     22  *   Bits [31:24] : Unused
     23  *
     24  * SBUS address format:
     25  *   Bits [15:0]  : Clause 45 register address
     26  *   Bits [18:16] : Lane control
     27  *   Bits [26:19] : Lane multicast (old format)
     28  *   Bits [31:27] : Clause 45 DEVAD
     29  *
     30  * The upper 16 bits if the SBUS address format is identical to the
     31  * Broadcom Address Extension Register (AER) format.
     32  *
     33  * The clause 45 indicator serves two purposes which is to ensure that
     34  * the upper 16 bits are never zero for a clause 45 address, but it
     35  * also makes it possible for the PHY bus driver to distinguish
     36  * between a clause 45 DEVAD and the old AER multicast format.
     37  */
     38 
     39 #include <phymod/phymod_system.h>
     40 #include <phymod/phymod_sim.h>
     41 
     42 /* Convenience macro */
     43 #define DBG_VERB PHYMOD_DEBUG_VERBOSE
     44 
     45 /* Bit field get/set macros */
     46 #define FALCON_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift))
     47 #define FALCON_BF_GET(_val, _mask, _shift) (((_val) >> (_shift)) & (_mask))
     48 
     49 /*
     50  * Raw 32-bit address consists of AER value in upper 16 bits and
     51  * clause 45 address in lower 16 bits.
     52  */
     53 #define FALCON_DEVAD_SHIFT       27
     54 #define FALCON_DEVAD_MASK        0x1f
     55 #define FALCON_DEVAD_GET(_addr) \
     56     FALCON_BF_GET(_addr, FALCON_DEVAD_MASK, FALCON_DEVAD_SHIFT)
     57 #define FALCON_LANE_SHIFT        16
     58 #define FALCON_LANE_MASK         0x7
     59 #define FALCON_LANE_GET(_addr) \
     60     FALCON_BF_GET(_addr, FALCON_LANE_MASK, FALCON_LANE_SHIFT)
     61 #define FALCON_REG_SHIFT         0
     62 #define FALCON_REG_MASK          0xffff
     63 #define FALCON_REG_GET(_addr) \
     64     FALCON_BF_GET(_addr, FALCON_REG_MASK, FALCON_REG_SHIFT)
     65 
     66 #define FALCON_ADDR(_devad, _lane, _reg) \
     67     (((_devad) << FALCON_DEVAD_SHIFT) +  \
     68      ((_lane) << FALCON_LANE_SHIFT) +    \
     69      ((_reg) << FALCON_REG_SHIFT))
     70 
     71 #define FALCON_AER               FALCON_ADDR(0, 0, 0xffde)
     72 #define FALCON_BLK               FALCON_ADDR(0, 0, 0x001f)
     73 
     74 /*RAM sim*/
     75 #define FALCON_RAM_WR_ADDR_REG_MS   (0xd204)          
     76 #define FALCON_RAM_WR_ADDR_REG_LS   (0xd205)
     77 #define FALCON_RAM_WR_DATA_REG_MS   (0xd206)
     78 #define FALCON_RAM_WR_DATA_REG_LS   (0xd207)
     79 #define FALCON_RAM_RD_ADDR_REG_MS   (0xd208)          
     80 #define FALCON_RAM_RD_ADDR_REG_LS   (0xd209)
     81 #define FALCON_RAM_RD_DATA_REG_MS   (0xd20a)
     82 #define FALCON_RAM_RD_DATA_REG_LS   (0xd20b)
     83 #define FALCON_IS_RAM_ADDR_REG(reg) (reg == FALCON_RAM_WR_ADDR_REG_MS || reg == FALCON_RAM_WR_ADDR_REG_LS || reg == FALCON_RAM_RD_ADDR_REG_MS || reg == FALCON_RAM_RD_ADDR_REG_LS)
     84 #define FALCON_IS_RAM_DATA_REG(reg) (reg == FALCON_RAM_WR_DATA_REG_MS || reg == FALCON_RAM_WR_DATA_REG_LS || reg == FALCON_RAM_RD_DATA_REG_MS || reg == FALCON_RAM_RD_DATA_REG_LS)
     85 
     86 #define FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY (0x1)
     87 #define FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY (0x2)
     88 #define FALCON_SIM_ENTRY_F_RAM_LS_ADDR_ENTRY (0x4)
     89 #define FALCON_SIM_ENTRY_F_RAM_MS_ADDR_ENTRY (0x8)
     90 
     91 
     92 
     93 /*
     94  * The CL45 indicator is used to determine whether the upper 16 bits
     95  * of the address is an AER value or a clause 45 DEVAD.
     96  */
     97 #define FALCON_CL45              (0x20 << 16)
     98 #define FALCON_CL45_MASK         (0xe0 << 16)
     99 
    100 STATIC uint32_t
    101 falcon_sim_default_data_get(uint32_t addr)
    102 {
    103     uint32_t devad, reg;
    104 
    105     devad = FALCON_DEVAD_GET(addr);
    106     reg = FALCON_REG_GET(addr);
    107 
    108     if (devad == 0) {
    109         switch (reg) {
    110         case 0x0002:
    111             return 0;
    112         case 0x0003:
    113             return 0;
    114         case 0xd100:
    115             return 0x02db;
    116         case 0xd108:
    117         case 0xd0b9:
    118             /*
    119              * Reset check
    120              */
    121             return 0x7;
    122         default:
    123             break;
    124         }
    125     }else if (devad == 1) {
    126         switch (reg) {
    127         case 0xd100:
    128             return 0x02db;
    129         default:
    130             break;
    131         }
    132     }
    133     return 0;
    134 }
    135 
    136 STATIC uint32_t
    137 falcon_sim_reg_copies_get(uint32_t addr)
    138 {
    139     uint32_t devad, reg;
    140 
    141     devad = FALCON_DEVAD_GET(addr);
    142     reg = FALCON_REG_GET(addr);
    143 
    144     if (reg == FALCON_AER || reg == FALCON_BLK) {
    145         return 1;
    146     }
    147 
    148     if (devad == 0) {
    149         if ((reg & 0xf000) == 0x9000) {
    150             return 1;
    151         }
    152         if ((reg & 0xf000) == 0xa000) {
    153             return 2;
    154         }
    155         return 4;
    156     } 
    157     return 0;
    158 }
    159 
    160 STATIC int
    161 falcon_sim_init(phymod_sim_data_t *pms_data,
    162                int num_entries, phymod_sim_entry_t *entries)
    163 {
    164     if (pms_data != NULL) {
    165         PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data));
    166         pms_data->num_entries = num_entries;
    167         pms_data->entries = entries;
    168     }
    169     return PHYMOD_E_NONE;
    170 }
    171 
    172 STATIC int
    173 falcon_sim_reset(phymod_sim_data_t *pms_data)
    174 {
    175     uint32_t sim_size;
    176 
    177     if (pms_data == NULL || pms_data->entries == NULL) {
    178         return PHYMOD_E_INIT;
    179     }
    180 
    181     pms_data->entries_used = 0;
    182     sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t);
    183     PHYMOD_MEMSET(pms_data->entries, 0, sim_size);
    184 
    185     return PHYMOD_E_NONE;
    186 }
    187 
    188 STATIC int
    189 falcon_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data)
    190 {
    191     int idx;
    192     uint32_t aer, blk, devad, reg, copies;
    193     uint32_t lane = 0;
    194     phymod_sim_entry_t *pse;
    195     uint32_t pse_flag = 0;
    196     uint32_t addr_ms, addr_ls;
    197 
    198     if (pms_data == NULL || pms_data->entries == NULL) {
    199         return PHYMOD_E_INIT;
    200     }
    201 
    202     devad = 0;
    203 
    204 
    205     if (addr < FALCON_BLK) {
    206         /* Assume clause 22 access */
    207         (void)falcon_sim_read(pms_data, FALCON_BLK, &blk);
    208         if (addr & 0x10) {
    209             /* IEEE bit */
    210             blk |= 0x8000;
    211         }
    212         addr = (addr & 0xf) | (blk & 0xfff0);
    213         if (addr != FALCON_AER && addr != FALCON_BLK) {
    214             (void)falcon_sim_read(pms_data, FALCON_AER, &aer);
    215             addr |= (aer << 16);
    216         }
    217     } else {
    218         /* Extract devad if clause 45 address format */
    219         if ((addr & FALCON_CL45_MASK) == FALCON_CL45) {
    220             devad = (addr >> 16) & 0x1f;
    221             addr &= 0xffff;
    222         }
    223     }
    224 
    225     if (addr != FALCON_AER && addr != FALCON_BLK) {
    226         /* Assume AER is in upper 16 bits */
    227         aer = (addr >> 16);
    228         if (aer == 0) {
    229             /* Try reading real AER instead */
    230             (void)falcon_sim_read(pms_data, FALCON_AER, &aer);
    231         }
    232         /* Add clause 45 devad (if used) */
    233         if (devad) {
    234             aer |= (devad << 11);
    235             addr = (addr & 0xffff) | (aer << 16);
    236         }
    237         lane = (aer & 0x7);
    238         if (lane > 3) {
    239             /* Force lane 0 if lane is invalid */
    240             addr = FALCON_ADDR(FALCON_DEVAD_GET(addr), 0, FALCON_REG_GET(addr));
    241         }
    242     }
    243 
    244     /* Adjust lane according to number of copies */
    245     devad = FALCON_DEVAD_GET(addr);
    246     reg = FALCON_REG_GET(addr);
    247     copies = falcon_sim_reg_copies_get(addr);
    248     if (copies == 1) {
    249         lane = 0;
    250     } else if (copies == 2) {
    251         lane &= ~0x1;
    252     }
    253     
    254 
    255     /*handle ram read/write*/
    256     if (FALCON_IS_RAM_DATA_REG(reg) || FALCON_IS_RAM_ADDR_REG(reg)) {
    257 
    258         if (FALCON_IS_RAM_DATA_REG(reg)) {
    259             (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_LS), &addr_ls);
    260             (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_MS), &addr_ms);
    261             addr = addr_ls | (addr_ms << 16);
    262             if (reg == FALCON_RAM_RD_DATA_REG_LS || reg == FALCON_RAM_WR_DATA_REG_LS) {
    263                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY;
    264             } else {
    265                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY;
    266             }
    267         } else { /*FALCON_IS_RAM_ADDR_REG*/
    268             addr = FALCON_ADDR(devad, lane, 0 /*dummy*/);
    269             if (reg == FALCON_RAM_RD_ADDR_REG_LS || reg == FALCON_RAM_WR_ADDR_REG_LS) {
    270                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY;
    271             } else {
    272                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY;
    273             }
    274         }
    275     
    276         /* Check if this register has been written already */
    277         for (idx = 0; idx < pms_data->entries_used; idx++) {
    278             pse = &pms_data->entries[idx];
    279             if ((pse->addr == addr) && (pse->flags == pse_flag)) {
    280                 *data = pse->data;
    281                 DBG_VERB(("falcon_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    282                           addr, *data));
    283                 return PHYMOD_E_NONE;
    284             }
    285         }
    286 
    287         *data = 0;
    288         return PHYMOD_E_NONE;
    289     }
    290 
    291 
    292 
    293     addr = FALCON_ADDR(devad, lane, reg);
    294 
    295     /* Check if this register has been written already */
    296     for (idx = 0; idx < pms_data->entries_used; idx++) {
    297         pse = &pms_data->entries[idx];
    298         if ((pse->addr == addr) && (pse->flags == pse_flag)) {
    299             *data = pse->data;
    300             DBG_VERB(("falcon_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    301                       addr, *data));
    302             return PHYMOD_E_NONE;
    303         }
    304     }
    305 
    306     /* Return default value if register was never written */
    307     *data = falcon_sim_default_data_get(addr);
    308 
    309     DBG_VERB(("falcon_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n",
    310               addr, *data));
    311 
    312     return PHYMOD_E_NONE;
    313 }
    314 
    315 STATIC int
    316 falcon_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    317 {
    318     int idx;
    319     uint32_t aer, blk, devad, reg, copies, mask;
    320     uint32_t lane = 0;
    321     phymod_sim_entry_t *pse;
    322     uint32_t pse_flag = 0;
    323     uint32_t addr_ms, addr_ls;
    324 
    325     if (pms_data == NULL || pms_data->entries == NULL) {
    326         return PHYMOD_E_INIT;
    327     }
    328 
    329     devad = 0;
    330 
    331     if (addr < FALCON_BLK) {
    332         /* Assume clause 22 access */
    333         (void)falcon_sim_read(pms_data, FALCON_BLK, &blk);
    334         if (addr & 0x10) {
    335             /* IEEE bit */
    336             blk |= 0x8000;
    337         }
    338         addr = (addr & 0xf) | (blk & 0xfff0);
    339         if (addr != FALCON_AER && addr != FALCON_BLK) {
    340             (void)falcon_sim_read(pms_data, FALCON_AER, &aer);
    341             addr |= (aer << 16);
    342         }
    343     } else {
    344         /* Extract devad if clause 45 address format */
    345         if ((addr & FALCON_CL45_MASK) == FALCON_CL45) {
    346             devad = (addr >> 16) & 0x1f;
    347             addr &= 0xffff;
    348         }
    349     }
    350 
    351     if (addr != FALCON_AER && addr != FALCON_BLK) {
    352         /* Assume AER is in upper 16 bits */
    353         aer = (addr >> 16);
    354         if (aer == 0) {
    355             /* Try reading real AER instead */
    356             (void)falcon_sim_read(pms_data, FALCON_AER, &aer);
    357         }
    358         /* Add clause 45 devad (if used) */
    359         if (devad) {
    360             aer |= (devad << 11);
    361             addr = (addr & 0xffff) | (aer << 16);
    362         }
    363         lane = (aer & 0x7);
    364         if (lane > 6) {
    365             return PHYMOD_E_PARAM;
    366         }
    367         if (lane > 3) {
    368             /*
    369              * Handle lane broadcast
    370              *
    371              * Note that we use lane 8 instead of lane 0 to prevent a
    372              * broadcast loop. The value 8 will become 0 when masked
    373              * with 0x7, but it prevents the AER in the upper 16 bits
    374              * from being zero, which will cause the code above to
    375              * obtain the AER value from register 0xffde.
    376              */
    377             reg = FALCON_REG_GET(addr);
    378             devad = FALCON_DEVAD_GET(addr);
    379             if (lane == 4 || lane == 6) {
    380                 /* Write lanes 0 and 1 */
    381                 addr = FALCON_ADDR(devad, 8, reg);
    382                 (void)falcon_sim_write(pms_data, addr, data);
    383                 addr = FALCON_ADDR(devad, 1, reg);
    384                 (void)falcon_sim_write(pms_data, addr, data);
    385             }
    386             if (lane == 5 || lane == 6) {
    387                 /* Write lanes 2 and 3 */
    388                 addr = FALCON_ADDR(devad, 2, reg);
    389                 (void)falcon_sim_write(pms_data, addr, data);
    390                 addr = FALCON_ADDR(devad, 3, reg);
    391                 (void)falcon_sim_write(pms_data, addr, data);
    392             }
    393             return PHYMOD_E_NONE;
    394         }
    395     }
    396 
    397     /* Adjust lane according to number of copies */
    398     devad = FALCON_DEVAD_GET(addr);
    399     reg = FALCON_REG_GET(addr);
    400     copies = falcon_sim_reg_copies_get(addr);
    401     if (copies == 1) {
    402         lane = 0;
    403     } else if (copies == 2) {
    404         lane &= ~0x1;
    405     }
    406 
    407     if (FALCON_IS_RAM_DATA_REG(reg) || FALCON_IS_RAM_ADDR_REG(reg)) {
    408 
    409         if (FALCON_IS_RAM_DATA_REG(reg)) {
    410             (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_LS), &addr_ls);
    411             (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_MS), &addr_ms);
    412             addr = addr_ls | (addr_ms << 16);
    413             if (reg == FALCON_RAM_RD_DATA_REG_LS || reg == FALCON_RAM_WR_DATA_REG_LS) {
    414                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY;
    415             } else {
    416                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY;
    417             }
    418         } else { /*FALCON_IS_RAM_ADDR_REG*/
    419             addr = FALCON_ADDR(devad, lane, 0/*dummy*/); 
    420             if (reg == FALCON_RAM_RD_ADDR_REG_LS || reg == FALCON_RAM_WR_ADDR_REG_LS) {
    421                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY;
    422             } else {
    423                 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY;
    424             }
    425         }
    426 
    427         /* Check if this register has been written already */
    428         for (idx = 0; idx < pms_data->entries_used; idx++) {
    429             pse = &pms_data->entries[idx];
    430             if ((pse->addr == addr) && (pse->flags == pse_flag)) {
    431                 pse->data = data;
    432                 DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" - flag = %u \n",
    433                           addr, pse->data, pse_flag));
    434                 return PHYMOD_E_NONE;
    435             }
    436         }
    437 
    438         pse = &pms_data->entries[pms_data->entries_used++];
    439         pse->addr = addr;
    440         pse->data = data;
    441         pse->flags = pse_flag;
    442 
    443         DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"  - flag = %u (new)\n",
    444               addr, pse->data, pse->flags));
    445 
    446         return PHYMOD_E_NONE;
    447     }
    448 
    449     addr = FALCON_ADDR(devad, lane, reg);
    450 
    451     /* Support optional write mask in upper 16 bits */
    452     mask = (data >> 16);
    453     if (mask == 0) {
    454         mask = 0xffff;
    455     }
    456     data &= mask;
    457 
    458 
    459 
    460     /* Check if this register has been written already */
    461     for (idx = 0; idx < pms_data->entries_used; idx++) {
    462         pse = &pms_data->entries[idx];
    463         if ((pse->addr == addr) && (pse->flags == pse_flag)) {
    464             pse->data &= ~mask;
    465             pse->data |= data;
    466             DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    467                       addr, pse->data));
    468             return PHYMOD_E_NONE;
    469         }
    470     }
    471 
    472     /* Check if database is full */
    473     if (pms_data->entries_used >= pms_data->num_entries) {
    474         return PHYMOD_E_RESOURCE;
    475     }
    476 
    477     /* Check if new data matches default value */
    478     if (data == falcon_sim_default_data_get(addr)) {
    479         return PHYMOD_E_NONE;
    480     }
    481 
    482     /* Add new register value */
    483     pse = &pms_data->entries[pms_data->entries_used++];
    484     pse->addr = addr;
    485     pse->data = data;
    486     pse->flags = pse_flag;
    487 
    488     DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n",
    489               addr, pse->data));
    490 
    491     return PHYMOD_E_NONE;
    492 }
    493 
    494 STATIC int
    495 falcon_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event)
    496 {
    497     if (pms_data == NULL || pms_data->entries == NULL) {
    498         return PHYMOD_E_INIT;
    499     }
    500 
    501     return PHYMOD_E_NONE;
    502 }
    503 
    504 phymod_sim_drv_t falcon_sim_drv = {
    505     falcon_sim_init,
    506     falcon_sim_reset,
    507     falcon_sim_read,
    508     falcon_sim_write,
    509     falcon_sim_event
    510 };
    511