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

tscf_sim.c (15744B)


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