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

eagle_sim.c (11083B)


      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 
     43 /* Convenience macro */
     44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE
     45 
     46 /* Bit field get/set macros */
     47 #define EAGLE_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift))
     48 #define EAGLE_BF_GET(_val, _mask, _shift) (((_val) >> (_shift)) & (_mask))
     49 
     50 /*
     51  * Raw 32-bit address consists of AER value in upper 16 bits and
     52  * clause 45 address in lower 16 bits.
     53  */
     54 #define EAGLE_DEVAD_SHIFT       27
     55 #define EAGLE_DEVAD_MASK        0x1f
     56 #define EAGLE_DEVAD_GET(_addr) \
     57     EAGLE_BF_GET(_addr, EAGLE_DEVAD_MASK, EAGLE_DEVAD_SHIFT)
     58 #define EAGLE_LANE_SHIFT        16
     59 #define EAGLE_LANE_MASK         0x7
     60 #define EAGLE_LANE_GET(_addr) \
     61     EAGLE_BF_GET(_addr, EAGLE_LANE_MASK, EAGLE_LANE_SHIFT)
     62 #define EAGLE_REG_SHIFT         0
     63 #define EAGLE_REG_MASK          0xffff
     64 #define EAGLE_REG_GET(_addr) \
     65     EAGLE_BF_GET(_addr, EAGLE_REG_MASK, EAGLE_REG_SHIFT)
     66 
     67 #define EAGLE_ADDR(_devad, _lane, _reg) \
     68     (((_devad) << EAGLE_DEVAD_SHIFT) +  \
     69      ((_lane) << EAGLE_LANE_SHIFT) +    \
     70      ((_reg) << EAGLE_REG_SHIFT))
     71 
     72 #define EAGLE_AER               EAGLE_ADDR(0, 0, 0xffde)
     73 #define EAGLE_BLK               EAGLE_ADDR(0, 0, 0x001f)
     74 
     75 /*
     76  * The CL45 indicator is used to determine whether the upper 16 bits
     77  * of the address is an AER value or a clause 45 DEVAD.
     78  */
     79 #define EAGLE_CL45              (0x20 << 16)
     80 #define EAGLE_CL45_MASK         (0xe0 << 16)
     81 
     82 STATIC uint32_t
     83 eagle_sim_default_data_get(uint32_t addr)
     84 {
     85     uint32_t devad, reg;
     86 
     87     devad = EAGLE_DEVAD_GET(addr);
     88     reg = EAGLE_REG_GET(addr);
     89 
     90     if (devad == 0) {
     91         switch (reg) {
     92         case 0x0002:
     93             return 0;
     94         case 0x0003:
     95             return 0;
     96         case 0x900e:
     97             return 0x02d2;
     98         case 0xd0f0:
     99             return 0x02da;
    100         default:
    101             break;
    102         }
    103     } else { 
    104         /*revid_model*/
    105         if (devad == 1 && reg == 0xd0f0) {
    106             return 0x1b;
    107         } else {
    108             return 0;
    109         }
    110     }
    111 
    112     return 0;
    113      
    114 }
    115 
    116 STATIC uint32_t
    117 eagle_sim_reg_copies_get(uint32_t addr)
    118 {
    119     uint32_t reg;
    120 
    121     reg = EAGLE_REG_GET(addr);
    122 
    123     if (reg == EAGLE_AER || reg == EAGLE_BLK) {
    124         return 1;
    125     }
    126 
    127     return 4;
    128 }
    129 
    130 STATIC int
    131 eagle_sim_init(phymod_sim_data_t *pms_data,
    132                int num_entries, phymod_sim_entry_t *entries)
    133 {
    134     if (pms_data != NULL) {
    135         PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data));
    136         pms_data->num_entries = num_entries;
    137         pms_data->entries = entries;
    138     }
    139     return PHYMOD_E_NONE;
    140 }
    141 
    142 STATIC int
    143 eagle_sim_reset(phymod_sim_data_t *pms_data)
    144 {
    145     uint32_t sim_size;
    146 
    147     if (pms_data == NULL || pms_data->entries == NULL) {
    148         return PHYMOD_E_INIT;
    149     }
    150 
    151     pms_data->entries_used = 0;
    152     sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t);
    153     PHYMOD_MEMSET(pms_data->entries, 0, sim_size);
    154 
    155     return PHYMOD_E_NONE;
    156 }
    157 
    158 STATIC int
    159 eagle_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data)
    160 {
    161     int idx;
    162     uint32_t aer, blk, devad, reg, copies;
    163     uint32_t lane = 0;
    164     phymod_sim_entry_t *pse;
    165 
    166     if (pms_data == NULL || pms_data->entries == NULL) {
    167         return PHYMOD_E_INIT;
    168     }
    169 
    170     devad = 0;
    171 
    172     if (addr < EAGLE_BLK) {
    173         /* Assume clause 22 access */
    174         (void)eagle_sim_read(pms_data, EAGLE_BLK, &blk);
    175         if (addr & 0x10) {
    176             /* IEEE bit */
    177             blk |= 0x8000;
    178         }
    179         addr = (addr & 0xf) | (blk & 0xfff0);
    180         if (addr != EAGLE_AER && addr != EAGLE_BLK) {
    181             (void)eagle_sim_read(pms_data, EAGLE_AER, &aer);
    182             addr |= (aer << 16);
    183         }
    184     } else {
    185         /* Extract devad if clause 45 address format */
    186         if ((addr & EAGLE_CL45_MASK) == EAGLE_CL45) {
    187             devad = (addr >> 16) & 0x1f;
    188             addr &= 0xffff;
    189         }
    190     }
    191 
    192     if (addr != EAGLE_AER && addr != EAGLE_BLK) {
    193         /* Assume AER is in upper 16 bits */
    194         aer = (addr >> 16);
    195         if (aer == 0) {
    196             /* Try reading real AER instead */
    197             (void)eagle_sim_read(pms_data, EAGLE_AER, &aer);
    198         }
    199         /* Add clause 45 devad (if used) */
    200         if (devad) {
    201             aer |= (devad << 11);
    202             addr = (addr & 0xffff) | (aer << 16);
    203         }
    204         lane = (aer & 0x7);
    205         if (lane > 3) {
    206             /* Force lane 0 if lane is invalid */
    207             addr = EAGLE_ADDR(EAGLE_DEVAD_GET(addr), 0, EAGLE_REG_GET(addr));
    208         }
    209     }
    210 
    211     /* Adjust lane according to number of copies */
    212     devad = EAGLE_DEVAD_GET(addr);
    213     reg = EAGLE_REG_GET(addr);
    214     copies = eagle_sim_reg_copies_get(addr);
    215     if (copies == 1) {
    216         lane = 0;
    217     } else if (copies == 2) {
    218         lane &= ~0x1;
    219     }
    220     addr = EAGLE_ADDR(devad, lane, reg);
    221 
    222     /* Check if this register has been written already */
    223     for (idx = 0; idx < pms_data->entries_used; idx++) {
    224         pse = &pms_data->entries[idx];
    225         if (pse->addr == addr) {
    226             *data = pse->data;
    227             DBG_VERB(("eagle_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    228                       addr, *data));
    229             return PHYMOD_E_NONE;
    230         }
    231     }
    232 
    233     /* Return default value if register was never written */
    234     *data = eagle_sim_default_data_get(addr);
    235 
    236     DBG_VERB(("eagle_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n",
    237               addr, *data));
    238 
    239     return PHYMOD_E_NONE;
    240 }
    241 
    242 STATIC int
    243 eagle_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    244 {
    245     int idx;
    246     uint32_t aer, blk, devad, reg, copies, mask;
    247     uint32_t lane = 0;
    248     phymod_sim_entry_t *pse;
    249 
    250     if (pms_data == NULL || pms_data->entries == NULL) {
    251         return PHYMOD_E_INIT;
    252     }
    253 
    254     devad = 0;
    255 
    256     if (addr < EAGLE_BLK) {
    257         /* Assume clause 22 access */
    258         (void)eagle_sim_read(pms_data, EAGLE_BLK, &blk);
    259         if (addr & 0x10) {
    260             /* IEEE bit */
    261             blk |= 0x8000;
    262         }
    263         addr = (addr & 0xf) | (blk & 0xfff0);
    264         if (addr != EAGLE_AER && addr != EAGLE_BLK) {
    265             (void)eagle_sim_read(pms_data, EAGLE_AER, &aer);
    266             addr |= (aer << 16);
    267         }
    268     } else {
    269         /* Extract devad if clause 45 address format */
    270         if ((addr & EAGLE_CL45_MASK) == EAGLE_CL45) {
    271             devad = (addr >> 16) & 0x1f;
    272             addr &= 0xffff;
    273         }
    274     }
    275 
    276     if (addr != EAGLE_AER && addr != EAGLE_BLK) {
    277         /* Assume AER is in upper 16 bits */
    278         aer = (addr >> 16);
    279         if (aer == 0) {
    280             /* Try reading real AER instead */
    281             (void)eagle_sim_read(pms_data, EAGLE_AER, &aer);
    282         }
    283         /* Add clause 45 devad (if used) */
    284         if (devad) {
    285             aer |= (devad << 11);
    286             addr = (addr & 0xffff) | (aer << 16);
    287         }
    288         lane = (aer & 0x7);
    289         if (lane > 6) {
    290             return PHYMOD_E_PARAM;
    291         }
    292         if (lane > 3) {
    293             /*
    294              * Handle lane broadcast
    295              *
    296              * Note that we use lane 8 instead of lane 0 to prevent a
    297              * broadcast loop. The value 8 will become 0 when masked
    298              * with 0x7, but it prevents the AER in the upper 16 bits
    299              * from being zero, which will cause the code above to
    300              * obtain the AER value from register 0xffde.
    301              */
    302             reg = EAGLE_REG_GET(addr);
    303             devad = EAGLE_DEVAD_GET(addr);
    304             if (lane == 4 || lane == 6) {
    305                 /* Write lanes 0 and 1 */
    306                 addr = EAGLE_ADDR(devad, 8, reg);
    307                 (void)eagle_sim_write(pms_data, addr, data);
    308                 addr = EAGLE_ADDR(devad, 1, reg);
    309                 (void)eagle_sim_write(pms_data, addr, data);
    310             }
    311             if (lane == 5 || lane == 6) {
    312                 /* Write lanes 2 and 3 */
    313                 addr = EAGLE_ADDR(devad, 2, reg);
    314                 (void)eagle_sim_write(pms_data, addr, data);
    315                 addr = EAGLE_ADDR(devad, 3, reg);
    316                 (void)eagle_sim_write(pms_data, addr, data);
    317             }
    318             return PHYMOD_E_NONE;
    319         }
    320     }
    321 
    322     /* Adjust lane according to number of copies */
    323     devad = EAGLE_DEVAD_GET(addr);
    324     reg = EAGLE_REG_GET(addr);
    325     copies = eagle_sim_reg_copies_get(addr);
    326     if (copies == 1) {
    327         lane = 0;
    328     } else if (copies == 2) {
    329         lane &= ~0x1;
    330     }
    331     addr = EAGLE_ADDR(devad, lane, reg);
    332 
    333     /* Support optional write mask in upper 16 bits */
    334     mask = (data >> 16);
    335     if (mask == 0) {
    336         mask = 0xffff;
    337     }
    338     data &= mask;
    339 
    340     /* Check if this register has been written already */
    341     for (idx = 0; idx < pms_data->entries_used; idx++) {
    342         pse = &pms_data->entries[idx];
    343         if (pse->addr == addr) {
    344             pse->data &= ~mask;
    345             pse->data |= data;
    346             DBG_VERB(("eagle_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    347                       addr, pse->data));
    348             return PHYMOD_E_NONE;
    349         }
    350     }
    351 
    352     /* Check if database is full */
    353     if (pms_data->entries_used >= pms_data->num_entries) {
    354         return PHYMOD_E_RESOURCE;
    355     }
    356 
    357     /* Check if new data matches default value */
    358     if (data == eagle_sim_default_data_get(addr)) {
    359         return PHYMOD_E_NONE;
    360     }
    361 
    362     /* Add new register value */
    363     pse = &pms_data->entries[pms_data->entries_used++];
    364     pse->addr = addr;
    365     pse->data = data;
    366 
    367     DBG_VERB(("eagle_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n",
    368               addr, pse->data));
    369 
    370     return PHYMOD_E_NONE;
    371 }
    372 
    373 STATIC int
    374 eagle_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event)
    375 {
    376     if (pms_data == NULL || pms_data->entries == NULL) {
    377         return PHYMOD_E_INIT;
    378     }
    379 
    380     return PHYMOD_E_NONE;
    381 }
    382 
    383 phymod_sim_drv_t eagle_sim_drv = {
    384     eagle_sim_init,
    385     eagle_sim_reset,
    386     eagle_sim_read,
    387     eagle_sim_write,
    388     eagle_sim_event
    389 };
    390