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

qsgmiie_sim.c (11239B)


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