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

qtce_sim.c (14165B)


      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  * Viper 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 QTCE_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift))
     47 #define QTCE_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 QTCE_DEVAD_SHIFT       27
     54 #define QTCE_DEVAD_MASK        0x1f
     55 #define QTCE_DEVAD_GET(_addr) \
     56     QTCE_BF_GET(_addr, QTCE_DEVAD_MASK, QTCE_DEVAD_SHIFT)
     57 #define QTCE_LANE_SHIFT        16
     58 #define QTCE_LANE_MASK         0x7
     59 #define QTCE_LANE_GET(_addr) \
     60     QTCE_BF_GET(_addr, QTCE_LANE_MASK, QTCE_LANE_SHIFT)
     61 #define QTCE_REG_SHIFT         0
     62 #define QTCE_REG_MASK          0xffff
     63 #define QTCE_REG_GET(_addr) \
     64     QTCE_BF_GET(_addr, QTCE_REG_MASK, QTCE_REG_SHIFT)
     65 
     66 #define QTCE_ADDR(_devad, _lane, _reg) \
     67     (((_devad) << QTCE_DEVAD_SHIFT) +  \
     68      ((_lane) << QTCE_LANE_SHIFT) +    \
     69      ((_reg) << QTCE_REG_SHIFT))
     70 
     71 #define QTCE_AER               QTCE_ADDR(0, 0, 0xffde)
     72 #define QTCE_BLK               QTCE_ADDR(0, 0, 0x001f)
     73 
     74 /*
     75  * The CL45 indicator is used to determine whether the upper 16 bits
     76  * of the address is an AER value or a clause 45 DEVAD.
     77  */
     78 #define QTCE_CL45              (0x20 << 16)
     79 #define QTCE_CL45_MASK         (0xe0 << 16)
     80 
     81 /* Forward declarations */
     82 STATIC int
     83 qtce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data);
     84 STATIC int
     85 qtce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data);
     86 
     87 STATIC uint32_t
     88 qtce_sim_default_data_get(uint32_t addr)
     89 {
     90     uint32_t devad, reg;
     91 
     92     devad = QTCE_DEVAD_GET(addr);
     93     reg = QTCE_REG_GET(addr);
     94 
     95     if (devad == 0) {
     96         switch (reg) {
     97         case 0x0002:
     98             return 0x600d;
     99         case 0x0003:
    100             return 0x8770;
    101         case 0x9007: /* MAIN_SERDESID */
    102             return 0x02d5;
    103         case 0xc041: /* sc_x4_final_config_status_sp(0)_A */
    104         case 0xc051: /* sc_x4_final_config_status_sp(1)_A */
    105         case 0xc061: /* sc_x4_final_config_status_sp(2)_A */
    106         case 0xc071: /* sc_x4_final_config_status_sp(3)_A */
    107             return 0x20;
    108         case 0xc020: /* sc_x4_control_control */
    109             return 0x1ff;
    110         case 0xc179: 
    111             return 0xff;
    112         default:
    113             break;
    114         }
    115     } else if (devad == 1) {
    116         switch (reg) {
    117         case 0xd205: /* UC_DOWNLOAD_STS */
    118             return 0x0080;
    119         case 0xd089:
    120             return 0x0007;
    121         case 0xd0f8:
    122             return 0x0007;
    123         default:
    124             break;
    125         }
    126     }
    127 
    128     return 0;
    129 }
    130 
    131 STATIC uint32_t
    132 qtce_sim_reg_copies_get(uint32_t addr)
    133 {
    134     uint32_t devad, reg;
    135 
    136     devad = QTCE_DEVAD_GET(addr);
    137     reg = QTCE_REG_GET(addr);
    138 
    139     if (reg == QTCE_AER || reg == QTCE_BLK) {
    140         return 1;
    141     }
    142 
    143     if (devad == 0) {
    144         if ((reg & 0xf000) == 0x9000) {
    145             return 1;
    146         }
    147         if ((reg & 0xf000) == 0xa000) {
    148             return 2;
    149         }
    150         return 4;
    151     } else if (devad == 1) {
    152         return 4;
    153     }
    154     return 0;
    155 }
    156 
    157 STATIC uint32_t
    158 qtce_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    159 {
    160     uint32_t devad, reg;
    161     uint32_t addr_tmp, data_tmp, data_r;
    162     uint32_t i, mask;
    163 
    164     devad = QTCE_DEVAD_GET(addr);
    165     reg = QTCE_REG_GET(addr);
    166 
    167     if (devad == 0) {
    168         switch (reg) {
    169         case 0xc020:
    170             /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */
    171             qtce_sim_write(pms_data, addr + 2, 0x3);
    172             /* Sync speed to  0xc040 sc_x4_final_config_status_sp0_resolved_speed */
    173             qtce_sim_write(pms_data, addr + 0x20, data & 0Xff);
    174             /* Set 0xc179 RX_X4_Status0_pcs_live_status and 0xc178 RX_X4_Status0_pcs_latched_status. force all ports link up */
    175             qtce_sim_write(pms_data, addr + 0x158, 0x0);
    176             qtce_sim_write(pms_data, addr + 0x159, 0xff);
    177             break;
    178         case 0xc021:
    179             mask = (data >> 16);
    180             if (mask == 0) {
    181                 mask = 0xffff;
    182             }
    183 
    184             qtce_sim_read(pms_data, addr, &data_r);
    185             data_r &= ~mask;
    186             data |= data_r;
    187 
    188             for (i = 0; i < 4; i++) {
    189                 addr_tmp = addr + 0x20 + i*0x10;  /* register 0xc041, 0xc051, 0xc061, 0xc071 */
    190                 data_tmp = (data >> i*2) & 0x3; /* 0: 1G, 1: 100M, 2: 10M  for 0xc021*/
    191                 data_tmp =  data_tmp == 0 ? 2 : data_tmp == 1 ? 0 : 1;  /* 0: 100M, 1: 10M, 2: 1G for 0xc041  */
    192                 qtce_sim_read(pms_data, addr_tmp, &data_r);
    193                 qtce_sim_write(pms_data, addr_tmp, ((data_r & 0xffcf) |  (data_tmp << 4)));
    194              }
    195             break;
    196         default:
    197             break;
    198         }
    199     } else if (devad == 1) {
    200         switch (reg) {
    201         default:
    202             break;
    203         }
    204     }
    205 
    206     return data;
    207 }
    208 
    209 STATIC int
    210 qtce_sim_init(phymod_sim_data_t *pms_data,
    211                int num_entries, phymod_sim_entry_t *entries)
    212 {
    213     if (pms_data != NULL) {
    214         PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data));
    215         pms_data->num_entries = num_entries;
    216         pms_data->entries = entries;
    217     }
    218     return PHYMOD_E_NONE;
    219 }
    220 
    221 STATIC int
    222 qtce_sim_reset(phymod_sim_data_t *pms_data)
    223 {
    224     uint32_t sim_size;
    225 
    226     if (pms_data == NULL || pms_data->entries == NULL) {
    227         return PHYMOD_E_INIT;
    228     }
    229 
    230     pms_data->entries_used = 0;
    231     sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t);
    232     PHYMOD_MEMSET(pms_data->entries, 0, sim_size);
    233 
    234     return PHYMOD_E_NONE;
    235 }
    236 
    237 STATIC int
    238 qtce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data)
    239 {
    240     int idx;
    241     uint32_t aer, blk, devad, reg, copies;
    242     uint32_t lane = 0;
    243     phymod_sim_entry_t *pse;
    244 
    245     if (pms_data == NULL || pms_data->entries == NULL) {
    246         return PHYMOD_E_INIT;
    247     }
    248 
    249     devad = 0;
    250 
    251     if (addr < QTCE_BLK) {
    252         /* Assume clause 22 access */
    253         (void)qtce_sim_read(pms_data, QTCE_BLK, &blk);
    254         /* IEEE bit */
    255         if (addr & 0x10) {
    256             blk |= 0x8000;
    257         } else {
    258             blk &= ~0x8000;
    259         }
    260         addr = (addr & 0xf) | (blk & 0xfff0);
    261         if (addr != QTCE_AER && addr != QTCE_BLK) {
    262             (void)qtce_sim_read(pms_data, QTCE_AER, &aer);
    263             addr |= (aer << 16);
    264         }
    265     } else {
    266         /* Extract devad if clause 45 address format */
    267         if ((addr & QTCE_CL45_MASK) == QTCE_CL45) {
    268             devad = (addr >> 16) & 0x1f;
    269             addr &= 0xffff;
    270         }
    271     }
    272 
    273     if (addr != QTCE_AER && addr != QTCE_BLK) {
    274         /* Assume AER is in upper 16 bits */
    275         aer = (addr >> 16);
    276         if (aer == 0) {
    277             /* Try reading real AER instead */
    278             (void)qtce_sim_read(pms_data, QTCE_AER, &aer);
    279         }
    280         /* Add clause 45 devad (if used) */
    281         if (devad) {
    282             aer |= (devad << 11);
    283             addr = (addr & 0xffff) | (aer << 16);
    284         }
    285         lane = (aer & 0x7);
    286         if (lane > 3) {
    287             /* Force lane 0 if lane is invalid */
    288             addr = QTCE_ADDR(QTCE_DEVAD_GET(addr), 0, QTCE_REG_GET(addr));
    289         }
    290     }
    291 
    292     /* Adjust lane according to number of copies */
    293     devad = QTCE_DEVAD_GET(addr);
    294     reg = QTCE_REG_GET(addr);
    295     copies = qtce_sim_reg_copies_get(addr);
    296     if (copies == 1) {
    297         lane = 0;
    298     } else if (copies == 2) {
    299         lane &= ~0x1;
    300     }
    301     addr = QTCE_ADDR(devad, lane, reg);
    302 
    303     if (addr == 0x800d00d) { /* DSC_UC_CTL:UC_DSC_READY_FOR_CMD */
    304         *data = 0x80;
    305         return PHYMOD_E_NONE;
    306     } else if (addr == 0x800d00e) { /* DSC_SCRATCH */
    307         *data = 0xf6c0; /* expected CRC */
    308         return PHYMOD_E_NONE;
    309     }
    310 
    311     /* Check if this register has been written already */
    312     for (idx = 0; idx < pms_data->entries_used; idx++) {
    313         pse = &pms_data->entries[idx];
    314         if (pse->addr == addr) {
    315             *data = pse->data;
    316             DBG_VERB(("qtce_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    317                       addr, *data));
    318             return PHYMOD_E_NONE;
    319         }
    320     }
    321 
    322     /* Return default value if register was never written */
    323     *data = qtce_sim_default_data_get(addr);
    324 
    325     DBG_VERB(("qtce_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n",
    326               addr, *data));
    327 
    328     return PHYMOD_E_NONE;
    329 }
    330 
    331 STATIC int
    332 qtce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    333 {
    334     int idx;
    335     uint32_t aer, blk, devad, reg, copies, mask;
    336     uint32_t lane = 0;
    337     phymod_sim_entry_t *pse;
    338 
    339     if (pms_data == NULL || pms_data->entries == NULL) {
    340         return PHYMOD_E_INIT;
    341     }
    342 
    343     devad = 0;
    344 
    345     if (addr < QTCE_BLK) {
    346         /* Assume clause 22 access */
    347         (void)qtce_sim_read(pms_data, QTCE_BLK, &blk);
    348         /* IEEE bit */
    349         if (addr & 0x10) {
    350             blk |= 0x8000;
    351         } else {
    352             blk &= ~0x8000;
    353         }
    354         addr = (addr & 0xf) | (blk & 0xfff0);
    355         if (addr != QTCE_AER && addr != QTCE_BLK) {
    356             (void)qtce_sim_read(pms_data, QTCE_AER, &aer);
    357             addr |= (aer << 16);
    358         }
    359     } else {
    360         /* Extract devad if clause 45 address format */
    361         if ((addr & QTCE_CL45_MASK) == QTCE_CL45) {
    362             devad = (addr >> 16) & 0x1f;
    363             addr &= 0xffff;
    364         }
    365     }
    366 
    367     if (addr != QTCE_AER && addr != QTCE_BLK) {
    368         /* Assume AER is in upper 16 bits */
    369         aer = (addr >> 16);
    370         if (aer == 0) {
    371             /* Try reading real AER instead */
    372             (void)qtce_sim_read(pms_data, QTCE_AER, &aer);
    373         }
    374         /* Add clause 45 devad (if used) */
    375         if (devad) {
    376             aer |= (devad << 11);
    377             addr = (addr & 0xffff) | (aer << 16);
    378         }
    379         lane = (aer & 0x7);
    380         if (lane > 6) {
    381             return PHYMOD_E_PARAM;
    382         }
    383         if (lane > 3) {
    384             /*
    385              * Handle lane broadcast
    386              *
    387              * Note that we use lane 8 instead of lane 0 to prevent a
    388              * broadcast loop. The value 8 will become 0 when masked
    389              * with 0x7, but it prevents the AER in the upper 16 bits
    390              * from being zero, which will cause the code above to
    391              * obtain the AER value from register 0xffde.
    392              */
    393             reg = QTCE_REG_GET(addr);
    394             devad = QTCE_DEVAD_GET(addr);
    395             if (lane == 4 || lane == 6) {
    396                 /* Write lanes 0 and 1 */
    397                 addr = QTCE_ADDR(devad, 8, reg);
    398                 (void)qtce_sim_write(pms_data, addr, data);
    399                 addr = QTCE_ADDR(devad, 1, reg);
    400                 (void)qtce_sim_write(pms_data, addr, data);
    401             }
    402             if (lane == 5 || lane == 6) {
    403                 /* Write lanes 2 and 3 */
    404                 addr = QTCE_ADDR(devad, 2, reg);
    405                 (void)qtce_sim_write(pms_data, addr, data);
    406                 addr = QTCE_ADDR(devad, 3, reg);
    407                 (void)qtce_sim_write(pms_data, addr, data);
    408             }
    409             return PHYMOD_E_NONE;
    410         }
    411     }
    412 
    413     /* Adjust data and/or related registers */
    414     data = qtce_sim_write_adjust(pms_data, addr, data);
    415 
    416     /* Adjust lane according to number of copies */
    417     devad = QTCE_DEVAD_GET(addr);
    418     reg = QTCE_REG_GET(addr);
    419     copies = qtce_sim_reg_copies_get(addr);
    420     if (copies == 1) {
    421         lane = 0;
    422     } else if (copies == 2) {
    423         lane &= ~0x1;
    424     }
    425     addr = QTCE_ADDR(devad, lane, reg);
    426 
    427     /* Support optional write mask in upper 16 bits */
    428     mask = (data >> 16);
    429     if (mask == 0) {
    430         mask = 0xffff;
    431     }
    432     data &= mask;
    433 
    434     /* Check if this register has been written already */
    435     for (idx = 0; idx < pms_data->entries_used; idx++) {
    436         pse = &pms_data->entries[idx];
    437         if (pse->addr == addr) {
    438             pse->data &= ~mask;
    439             pse->data |= data;
    440             DBG_VERB(("qtce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    441                       addr, pse->data));
    442             return PHYMOD_E_NONE;
    443         }
    444     }
    445 
    446     /* Check if database is full */
    447     if (pms_data->entries_used >= pms_data->num_entries) {
    448         return PHYMOD_E_RESOURCE;
    449     }
    450 
    451     /* Check if new data matches default value */
    452     if (data == qtce_sim_default_data_get(addr)) {
    453         return PHYMOD_E_NONE;
    454     }
    455 
    456     /* Add new register value */
    457     pse = &pms_data->entries[pms_data->entries_used++];
    458     pse->addr = addr;
    459     pse->data = data;
    460 
    461     DBG_VERB(("qtce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n",
    462               addr, pse->data));
    463 
    464     return PHYMOD_E_NONE;
    465 }
    466 
    467 STATIC int
    468 qtce_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event)
    469 {
    470     if (pms_data == NULL || pms_data->entries == NULL) {
    471         return PHYMOD_E_INIT;
    472     }
    473 
    474     return PHYMOD_E_NONE;
    475 }
    476 
    477 phymod_sim_drv_t qtce_sim_drv = {
    478     qtce_sim_init,
    479     qtce_sim_reset,
    480     qtce_sim_read,
    481     qtce_sim_write,
    482     qtce_sim_event
    483 };
    484