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

tscf16_sim.c (16503B)


      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 TSCF16_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift))
     47 #define TSCF16_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 TSCF16_DEVAD_SHIFT       27
     54 #define TSCF16_DEVAD_MASK        0x1f
     55 #define TSCF16_DEVAD_GET(_addr) \
     56     TSCF16_BF_GET(_addr, TSCF16_DEVAD_MASK, TSCF16_DEVAD_SHIFT)
     57 #define TSCF16_LANE_SHIFT        16
     58 #define TSCF16_LANE_MASK         0x7
     59 #define TSCF16_LANE_GET(_addr) \
     60     TSCF16_BF_GET(_addr, TSCF16_LANE_MASK, TSCF16_LANE_SHIFT)
     61 #define TSCF16_REG_SHIFT         0
     62 #define TSCF16_REG_MASK          0xffff
     63 #define TSCF16_REG_GET(_addr) \
     64     TSCF16_BF_GET(_addr, TSCF16_REG_MASK, TSCF16_REG_SHIFT)
     65 
     66 #define TSCF16_ADDR(_devad, _lane, _reg) \
     67     (((_devad) << TSCF16_DEVAD_SHIFT) +  \
     68      ((_lane) << TSCF16_LANE_SHIFT) +    \
     69      ((_reg) << TSCF16_REG_SHIFT))
     70 
     71 #define TSCF16_AER               TSCF16_ADDR(0, 0, 0xffde)
     72 #define TSCF16_BLK               TSCF16_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 TSCF16_CL45              (0x20 << 16)
     79 #define TSCF16_CL45_MASK         (0xe0 << 16)
     80 
     81 #define TSCF16_ID0                 0x600d
     82 #define TSCF16_ID1                 0x8770
     83 #define TSCF16_SERDES_ID           0x0315
     84 #define FALCON16_TSC_UCODE_IMAGE_CRC 0xe60a
     85 
     86 #define TSCF16_SIM_FW_LOAD_DONE_REG_ADDR (0x800d203)
     87 
     88 /* Forward declarations */
     89 STATIC int
     90 tscf16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data);
     91 STATIC int
     92 tscf16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data);
     93 
     94 STATIC uint32_t
     95 tscf16_sim_default_data_get(uint32_t addr)
     96 {
     97     uint32_t reg;
     98 
     99     reg = TSCF16_REG_GET(addr);
    100 
    101     /*force FW load success */
    102     if(addr == TSCF16_SIM_FW_LOAD_DONE_REG_ADDR) {
    103         return 1;
    104     }
    105 
    106     switch (reg) {
    107         case 0x0002:
    108             return TSCF16_ID0;
    109         case 0x0003:
    110             return TSCF16_ID1;
    111         case 0x900e:
    112             return TSCF16_SERDES_ID;
    113         case 0xd03d:
    114             /*
    115              * This is DSC_A_DSC_UC_CTRL for Falcon. Make sure
    116              * The 'ready' bit is set.
    117              */
    118             return 0x0080 ;
    119         case 0xd10a:
    120             /* DIG_COM_REVID1[15:12], Number of lanes */
    121             return 0x403e;
    122         case 0xd108:
    123         case 0xd0b9:
    124             /*
    125              * Reset check
    126              */
    127             return 0x7;
    128         case 0xd1b9:
    129             return 0x7;
    130         case 0xd104:
    131             return 0x8271;
    132         case 0xd170:
    133             return 0xb000;
    134         default:
    135             break;
    136     }
    137 
    138     return 0;
    139 }
    140 STATIC uint32_t
    141 tscf16_sim_reg_copies_get(uint32_t addr)
    142 {
    143     uint32_t devad, reg;
    144 
    145     devad = TSCF16_DEVAD_GET(addr);
    146     reg = TSCF16_REG_GET(addr);
    147 
    148     if (reg == TSCF16_AER || reg == TSCF16_BLK) {
    149         return 1;
    150     }
    151 
    152     if (devad == 0) {
    153         if ((reg & 0xf000) == 0x9000) {
    154             return 1;
    155         }
    156         if ((reg & 0xf000) == 0xa000) {
    157             return 2;
    158         }
    159         return 4;
    160     } else if (devad == 1) {
    161         return 4;
    162     }
    163     return 0;
    164 }
    165 
    166 STATIC int
    167 _tscf16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    168 {
    169     int idx;
    170     uint32_t mask;
    171 
    172     phymod_sim_entry_t *pse;
    173 
    174     if (pms_data == NULL || pms_data->entries == NULL) {
    175         return PHYMOD_E_INIT;
    176     }
    177 
    178     /* Support optional write mask in upper 16 bits */
    179     mask = (data >> 16);
    180     if (mask == 0) {
    181         mask = 0xffff;
    182     }
    183     data &= mask;
    184 
    185     /* Check if this register has been written already */
    186     for (idx = 0; idx < pms_data->entries_used; idx++) {
    187         pse = &pms_data->entries[idx];
    188         if (pse->addr == addr) {
    189             pse->data &= ~mask;
    190             pse->data |= data;
    191             DBG_VERB(("_tscf16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    192                       addr, pse->data));
    193             return PHYMOD_E_NONE;
    194         }
    195     }
    196 
    197     /* Check if database is full */
    198     if (pms_data->entries_used >= pms_data->num_entries) {
    199         return PHYMOD_E_RESOURCE;
    200     }
    201 
    202     /* Check if new data matches default value */
    203     if (data == tscf16_sim_default_data_get(addr)) {
    204         return PHYMOD_E_NONE;
    205     }
    206 
    207     /* Add new register value */
    208     pse = &pms_data->entries[pms_data->entries_used++];
    209     pse->addr = addr;
    210     pse->data = data;
    211 
    212     DBG_VERB(("_tscf16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n",
    213               addr, pse->data));
    214 
    215     return PHYMOD_E_NONE;
    216 }
    217 
    218 STATIC uint32_t
    219 tscf16_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    220 {
    221     uint32_t devad, reg, val;
    222 
    223     devad = TSCF16_DEVAD_GET(addr);
    224     reg = TSCF16_REG_GET(addr);
    225 
    226     if (devad == 0) {
    227         switch (reg) {
    228         case 0xc050:
    229             /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */
    230             tscf16_sim_write(pms_data, addr + 1, 0x3);
    231             /* Force link up in RX_X4_PCS_LIVE_STS register
    232                offset 0x111 = 0xc161 - 0xc050 */
    233             tscf16_sim_write(pms_data, addr + 0x111, 0x2);
    234             /* Sync configured speed into final speed status register
    235              * 0x20 = 0xc070 - 0xc050 */
    236             tscf16_sim_read(pms_data, addr + 0x20, &val);
    237             val = (val & 0xffff00ff) | ((data & 0xff)<<8);
    238             tscf16_sim_write(pms_data, addr + 0x20, val);
    239             break;
    240         case 0xc055:
    241             /* Sync configured Lane Number into Lane Number status register
    242              * 0x1B = 0xc070 - 0xc055 */
    243             tscf16_sim_read(pms_data, addr + 0x1B, &val);
    244             val = (val & 0xfffffff8) | (data & 0x7);
    245             tscf16_sim_write(pms_data, addr + 0x1B, val);
    246             break;
    247         default:
    248             break;
    249         }
    250     } else if (devad == 1) {
    251         switch (reg) {
    252         case 0xd204:
    253         case 0xd205:
    254         case 0xd206:
    255         case 0xd207:
    256             /* Sync configured aMICRO_A_COM registers. Write and read access different register
    257              * Write registers: 0xd204~0xd207; Read registers: 0xd208~0xd20b
    258              * 0x4 = 0xd208(~0xd20b) - 0xd204(~0xd207)*/
    259             tscf16_sim_write(pms_data, addr + 0x4, data);
    260             break;
    261         case 0xd0e1:
    262             /* Sync configured SIGDET_CTL1 register. When a port is set disable,
    263              * the port's RX_X4_PCS_LIVE_STS1 register deletes link status.
    264              * RX_X4_PCS_LIVE_STS1 register is PCS register, so need set devad to 0.
    265              * 0xF80 = 0xd0e1 - 0xc161*/
    266             tscf16_sim_read(pms_data, (addr - 0xF80) & 0x7ffff, &val);
    267             val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2);
    268             tscf16_sim_write(pms_data, (addr - 0xF80) & 0x7ffff, val);
    269             break;
    270         case 0xd147:
    271             /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */
    272             /* Write lanes 0, 1, 2 and 3 */
    273             addr = TSCF16_ADDR(devad, 0, reg);
    274             (void)_tscf16_sim_write(pms_data, addr, data);
    275 
    276             addr = TSCF16_ADDR(devad, 1, reg);
    277             (void)_tscf16_sim_write(pms_data, addr, data);
    278 
    279             addr = TSCF16_ADDR(devad, 2, reg);
    280             (void)_tscf16_sim_write(pms_data, addr, data);
    281 
    282             addr = TSCF16_ADDR(devad, 3, reg);
    283             (void)_tscf16_sim_write(pms_data, addr, data);
    284             break;
    285         case 0xd094:
    286         case 0xd095:
    287             /* Sync configured CL93N72 Control 2/3 registers. Write and read access different register
    288              * Write registers: 0xd094/d095; Read registers: 0xd133/0xd134
    289              * 0x9f = 0xd133(0xd134) - 0xd094(0xd095)*/
    290             tscf16_sim_write(pms_data, addr + 0x9f, data);
    291             break;
    292         default:
    293             break;
    294         }
    295     }
    296 
    297     return data;
    298 }
    299 
    300 STATIC int
    301 tscf16_sim_init(phymod_sim_data_t *pms_data,
    302                int num_entries, phymod_sim_entry_t *entries)
    303 {
    304     if (pms_data != NULL) {
    305         PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data));
    306         pms_data->num_entries = num_entries;
    307         pms_data->entries = entries;
    308     }
    309     return PHYMOD_E_NONE;
    310 }
    311 
    312 STATIC int
    313 tscf16_sim_reset(phymod_sim_data_t *pms_data)
    314 {
    315     uint32_t sim_size;
    316 
    317     if (pms_data == NULL || pms_data->entries == NULL) {
    318         return PHYMOD_E_INIT;
    319     }
    320 
    321     pms_data->entries_used = 0;
    322     sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t);
    323     PHYMOD_MEMSET(pms_data->entries, 0, sim_size);
    324 
    325     return PHYMOD_E_NONE;
    326 }
    327 
    328 STATIC int
    329 tscf16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data)
    330 {
    331     int idx;
    332     uint32_t aer, blk, devad, reg, copies;
    333     uint32_t lane = 0;
    334     phymod_sim_entry_t *pse;
    335 
    336     if (pms_data == NULL || pms_data->entries == NULL) {
    337         return PHYMOD_E_INIT;
    338     }
    339 
    340     devad = 0;
    341 
    342     if (addr < TSCF16_BLK) {
    343         /* Assume clause 22 access */
    344         (void)tscf16_sim_read(pms_data, TSCF16_BLK, &blk);
    345         /* IEEE bit */
    346         if (addr & 0x10) {
    347             blk |= 0x8000;
    348         } else {
    349             blk &= ~0x8000;
    350         }
    351         addr = (addr & 0xf) | (blk & 0xfff0);
    352         if (addr != TSCF16_AER && addr != TSCF16_BLK) {
    353             (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer);
    354             addr |= (aer << 16);
    355         }
    356     } else {
    357         /* Extract devad if clause 45 address format */
    358         if ((addr & TSCF16_CL45_MASK) == TSCF16_CL45) {
    359             devad = (addr >> 16) & 0x1f;
    360             addr &= 0xffff;
    361         }
    362     }
    363 
    364     if (addr != TSCF16_AER && addr != TSCF16_BLK) {
    365         /* Assume AER is in upper 16 bits */
    366         aer = (addr >> 16);
    367         if (aer == 0) {
    368             /* Try reading real AER instead */
    369             (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer);
    370         }
    371         /* Add clause 45 devad (if used) */
    372         if (devad) {
    373             aer |= (devad << 11);
    374             addr = (addr & 0xffff) | (aer << 16);
    375         }
    376         lane = (aer & 0x7);
    377         if (lane > 3) {
    378             /* Force lane 0 if lane is invalid */
    379             addr = TSCF16_ADDR(TSCF16_DEVAD_GET(addr), 0, TSCF16_REG_GET(addr));
    380         }
    381     }
    382 
    383     /* Adjust lane according to number of copies */
    384     devad = TSCF16_DEVAD_GET(addr);
    385     reg = TSCF16_REG_GET(addr);
    386     copies = tscf16_sim_reg_copies_get(addr);
    387     if (copies == 1) {
    388         lane = 0;
    389     } else if (copies == 2) {
    390         lane &= ~0x1;
    391     }
    392     addr = TSCF16_ADDR(devad, lane, reg);
    393 
    394     /* for DSC_UC_CTRL always succeed */
    395     if(addr == 0x800d03d) {
    396         *data = 0x80;
    397         DBG_VERB(("tscf16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    398                       addr, *data));
    399         return PHYMOD_E_NONE;
    400     } else if (addr == 0x800d03e) {
    401         *data = FALCON16_TSC_UCODE_IMAGE_CRC;
    402         return PHYMOD_E_NONE;
    403     } else if (reg == 0xd104) {
    404         *data = 0x8271;
    405         return PHYMOD_E_NONE;
    406     } else if (addr == 0x800d201) {
    407         *data = 0;
    408         return PHYMOD_E_NONE;
    409     }
    410 
    411     /* Check if this register has been written already */
    412     for (idx = 0; idx < pms_data->entries_used; idx++) {
    413         pse = &pms_data->entries[idx];
    414         if (pse->addr == addr) {
    415             *data = pse->data;
    416             DBG_VERB(("tscf16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    417                       addr, *data));
    418             return PHYMOD_E_NONE;
    419         }
    420     }
    421 
    422     /* Return default value if register was never written */
    423     *data = tscf16_sim_default_data_get(addr);
    424 
    425     DBG_VERB(("tscf16_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n",
    426               addr, *data));
    427 
    428     return PHYMOD_E_NONE;
    429 }
    430 
    431 STATIC int
    432 tscf16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    433 {
    434     uint32_t aer, blk, devad, reg, copies;
    435     uint32_t lane = 0;
    436 
    437     if (pms_data == NULL || pms_data->entries == NULL) {
    438         return PHYMOD_E_INIT;
    439     }
    440 
    441     devad = 0;
    442 
    443     if (addr < TSCF16_BLK) {
    444         /* Assume clause 22 access */
    445         (void)tscf16_sim_read(pms_data, TSCF16_BLK, &blk);
    446         /* IEEE bit */
    447         if (addr & 0x10) {
    448             blk |= 0x8000;
    449         } else {
    450             blk &= ~0x8000;
    451         }
    452         addr = (addr & 0xf) | (blk & 0xfff0);
    453         if (addr != TSCF16_AER && addr != TSCF16_BLK) {
    454             (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer);
    455             addr |= (aer << 16);
    456         }
    457     } else {
    458         /* Extract devad if clause 45 address format */
    459         if ((addr & TSCF16_CL45_MASK) == TSCF16_CL45) {
    460             devad = (addr >> 16) & 0x1f;
    461             addr &= 0xffff;
    462         }
    463     }
    464 
    465     if (addr != TSCF16_AER && addr != TSCF16_BLK) {
    466         /* Assume AER is in upper 16 bits */
    467         aer = (addr >> 16);
    468         if (aer == 0) {
    469             /* Try reading real AER instead */
    470             (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer);
    471         }
    472         /* Add clause 45 devad (if used) */
    473         if (devad) {
    474             aer |= (devad << 11);
    475             addr = (addr & 0xffff) | (aer << 16);
    476         }
    477         lane = (aer & 0x7);
    478         if (lane > 6) {
    479             return PHYMOD_E_PARAM;
    480         }
    481         if (lane > 3) {
    482             /*
    483              * Handle lane broadcast
    484              *
    485              * Note that we use lane 8 instead of lane 0 to prevent a
    486              * broadcast loop. The value 8 will become 0 when masked
    487              * with 0x7, but it prevents the AER in the upper 16 bits
    488              * from being zero, which will cause the code above to
    489              * obtain the AER value from register 0xffde.
    490              */
    491             reg = TSCF16_REG_GET(addr);
    492             devad = TSCF16_DEVAD_GET(addr);
    493             if (lane == 4 || lane == 6) {
    494                 /* Write lanes 0 and 1 */
    495                 addr = TSCF16_ADDR(devad, 8, reg);
    496                 (void)tscf16_sim_write(pms_data, addr, data);
    497                 addr = TSCF16_ADDR(devad, 1, reg);
    498                 (void)tscf16_sim_write(pms_data, addr, data);
    499             }
    500             if (lane == 5 || lane == 6) {
    501                 /* Write lanes 2 and 3 */
    502                 addr = TSCF16_ADDR(devad, 2, reg);
    503                 (void)tscf16_sim_write(pms_data, addr, data);
    504                 addr = TSCF16_ADDR(devad, 3, reg);
    505                 (void)tscf16_sim_write(pms_data, addr, data);
    506             }
    507             return PHYMOD_E_NONE;
    508         }
    509     }
    510 
    511     /* Adjust data and/or related registers */
    512     data = tscf16_sim_write_adjust(pms_data, addr, data);
    513 
    514     /* Adjust lane according to number of copies */
    515     devad = TSCF16_DEVAD_GET(addr);
    516     reg = TSCF16_REG_GET(addr);
    517     copies = tscf16_sim_reg_copies_get(addr);
    518     if (copies == 1) {
    519         lane = 0;
    520     } else if (copies == 2) {
    521         lane &= ~0x1;
    522     }
    523     addr = TSCF16_ADDR(devad, lane, reg);
    524 
    525     return _tscf16_sim_write(pms_data, addr, data);
    526 }
    527 
    528 STATIC int
    529 tscf16_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event)
    530 {
    531     if (pms_data == NULL || pms_data->entries == NULL) {
    532         return PHYMOD_E_INIT;
    533     }
    534 
    535     return PHYMOD_E_NONE;
    536 }
    537 
    538 phymod_sim_drv_t tscf16_sim_drv = {
    539     tscf16_sim_init,
    540     tscf16_sim_reset,
    541     tscf16_sim_read,
    542     tscf16_sim_write,
    543     tscf16_sim_event
    544 };
    545