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

tsce_sim.c (15209B)


      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 #include <phymod/chip/bcmi_tsce_xgxs_resetval.h>
     42 
     43 /* Convenience macro */
     44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE
     45 
     46 /* Bit field get/set macros */
     47 #define TSCE_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift))
     48 #define TSCE_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 TSCE_DEVAD_SHIFT       27
     55 #define TSCE_DEVAD_MASK        0x1f
     56 #define TSCE_DEVAD_GET(_addr) \
     57     TSCE_BF_GET(_addr, TSCE_DEVAD_MASK, TSCE_DEVAD_SHIFT)
     58 #define TSCE_LANE_SHIFT        16
     59 #define TSCE_LANE_MASK         0x7
     60 #define TSCE_LANE_GET(_addr) \
     61     TSCE_BF_GET(_addr, TSCE_LANE_MASK, TSCE_LANE_SHIFT)
     62 #define TSCE_REG_SHIFT         0
     63 #define TSCE_REG_MASK          0xffff
     64 #define TSCE_REG_GET(_addr) \
     65     TSCE_BF_GET(_addr, TSCE_REG_MASK, TSCE_REG_SHIFT)
     66 #define TSCE_PLLIDX_SHIF     24
     67 #define TSCE_PLLIDX_MASK     0x3
     68 #define TSCE_PLLIDX_GET(_addr) \
     69     TSCE_BF_GET(_addr, TSCE_PLLIDX_MASK, TSCE_PLLIDX_SHIF)
     70 
     71 #define TSCE_ADDR(_devad, _lane, _reg, _pllidx) \
     72     (((_devad) << TSCE_DEVAD_SHIFT) +  \
     73      ((_lane) << TSCE_LANE_SHIFT) +    \
     74      ((_reg) << TSCE_REG_SHIFT)) +     \
     75      ((_pllidx << TSCE_PLLIDX_SHIF))
     76 
     77 #define TSCE_AER               TSCE_ADDR(0, 0, 0xffde, 0)
     78 #define TSCE_BLK               TSCE_ADDR(0, 0, 0x001f, 0)
     79 
     80 /*
     81  * The CL45 indicator is used to determine whether the upper 16 bits
     82  * of the address is an AER value or a clause 45 DEVAD.
     83  */
     84 #define TSCE_CL45              (0x20 << 16)
     85 #define TSCE_CL45_MASK         (0xe0 << 16)
     86 
     87 #define TSCE_SIM_FW_LOAD_DONE_REG_ADDR (0x0800d205)
     88 #define TSCE_SIM_DSC_UC_CTRL_REG_ADDR (0x0800d00d)
     89 
     90 /* Forward declarations */
     91 STATIC int
     92 tsce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data);
     93 STATIC int
     94 tsce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data);
     95 
     96 STATIC uint32_t
     97 tsce_sim_default_data_get(uint32_t addr)
     98 {
     99     uint32_t devad, reg;
    100 
    101     devad = TSCE_DEVAD_GET(addr);
    102     reg = TSCE_REG_GET(addr);
    103 
    104     /*force FW load success */
    105     if(addr == TSCE_SIM_FW_LOAD_DONE_REG_ADDR) {
    106         return (1 << 15);
    107     }
    108 
    109     return bcmi_tsce_xgxs_resetval_get(devad, reg);
    110 }
    111 
    112 STATIC uint32_t
    113 tsce_sim_reg_copies_get(uint32_t addr)
    114 {
    115     uint32_t devad, reg;
    116 
    117     devad = TSCE_DEVAD_GET(addr);
    118     reg = TSCE_REG_GET(addr);
    119 
    120     if (reg == TSCE_AER || reg == TSCE_BLK) {
    121         return 1;
    122     }
    123 
    124     if (devad == 0) {
    125         if ((reg & 0xf000) == 0x9000) {
    126             return 1;
    127         }
    128         if ((reg & 0xf000) == 0xa000) {
    129             return 2;
    130         }
    131         return 4;
    132     } else if (devad == 1) {
    133         return 4;
    134     }
    135     return 0;
    136 }
    137 
    138 STATIC int
    139 _tsce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    140 {
    141     int idx;
    142     uint32_t mask;
    143 
    144     phymod_sim_entry_t *pse;
    145 
    146     if (pms_data == NULL || pms_data->entries == NULL) {
    147         return PHYMOD_E_INIT;
    148     }
    149 
    150     /* Support optional write mask in upper 16 bits */
    151     mask = (data >> 16);
    152     if (mask == 0) {
    153         mask = 0xffff;
    154     }
    155     data &= mask;
    156 
    157     /* Check if this register has been written already */
    158     for (idx = 0; idx < pms_data->entries_used; idx++) {
    159         pse = &pms_data->entries[idx];
    160         if (pse->addr == addr) {
    161             pse->data &= ~mask;
    162             pse->data |= data;
    163             DBG_VERB(("_tsce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    164                       addr, pse->data));
    165             return PHYMOD_E_NONE;
    166         }
    167     }
    168 
    169     /* Check if database is full */
    170     if (pms_data->entries_used >= pms_data->num_entries) {
    171         return PHYMOD_E_RESOURCE;
    172     }
    173 
    174     /* Check if new data matches default value */
    175     if (data == tsce_sim_default_data_get(addr)) {
    176         return PHYMOD_E_NONE;
    177     }
    178 
    179     /* Add new register value */
    180     pse = &pms_data->entries[pms_data->entries_used++];
    181     pse->addr = addr;
    182     pse->data = data;
    183 
    184     DBG_VERB(("_tsce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n",
    185               addr, pse->data));
    186 
    187     return PHYMOD_E_NONE;
    188 }
    189 
    190 STATIC uint32_t
    191 tsce_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    192 {
    193     uint32_t devad, reg, val;
    194        uint32_t pllidx = 0;
    195 
    196     devad = TSCE_DEVAD_GET(addr);
    197     reg = TSCE_REG_GET(addr);
    198        pllidx = TSCE_PLLIDX_GET(addr);
    199 
    200     if (devad == 0) {
    201         switch (reg) {
    202         case 0xc050:
    203             /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */
    204             tsce_sim_write(pms_data, addr + 1, 0x3);
    205             /* Force link up in RX_X4_PCS_LIVE_STS register
    206                offset 0x104 = 0xc154 - 0xc050 */
    207             tsce_sim_write(pms_data, addr + 0x104, 0x1b);
    208             /* Sync configured speed into final speed status register 0x20 = 0xc070 - 0xc050 */
    209             tsce_sim_read(pms_data, addr + 0x20, &val);
    210             val = (val & 0xffff00ff) | ((data & 0xff)<<8);
    211             tsce_sim_write(pms_data, addr + 0x20, val);
    212             break;
    213         case 0xc055:
    214             /* Sync configured Lane Number into Lane Number status register 0x1B = 0xc070 - 0xc055 */
    215             tsce_sim_read(pms_data, addr + 0x1B, &val);
    216             val = (val & 0xfffffff8) | (data & 0x7);
    217             tsce_sim_write(pms_data, addr + 0x1B, val);
    218             break;
    219         default:
    220             break;
    221         }
    222     } else if (devad == 1) {
    223         switch (reg) {
    224         case 0xd127:
    225             /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */
    226             /* Write lanes 0, 1, 2 and 3 */
    227             addr = TSCE_ADDR(devad, 0, reg, pllidx);
    228             (void)_tsce_sim_write(pms_data, addr, data);
    229 
    230             addr = TSCE_ADDR(devad, 1, reg, pllidx);
    231             (void)_tsce_sim_write(pms_data, addr, data);
    232 
    233             addr = TSCE_ADDR(devad, 2, reg, pllidx);
    234             (void)_tsce_sim_write(pms_data, addr, data);
    235 
    236             addr = TSCE_ADDR(devad, 3, reg, pllidx);
    237             (void)_tsce_sim_write(pms_data, addr, data);
    238             break;
    239         case 0xd203:
    240             /* write data register to UC_RAM_WRDATA(0xd203)
    241              * read data register from UC_RAM_RDDATA(0xd204)
    242              */
    243             _tsce_sim_write(pms_data, addr + 1, data);
    244             break;
    245         case 0xd0c1:
    246             /* Sync configured SIGDET_CTL1 register. When a port is set disable,
    247              * the port's RX_X4_PCS_LIVE_STS register deletes link status.
    248              * RX_X4_PCS_LIVE_STS register is PCS register, so need set devad to 0.
    249              * 0xF6D = 0xd0c1 - 0xc154*/
    250             tsce_sim_read(pms_data, (addr - 0xF6D) & 0x7ffff, &val);
    251             val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2);
    252             tsce_sim_write(pms_data, (addr - 0xF6D) & 0x7ffff, val);
    253             break;
    254         default:
    255             break;
    256         }
    257     }
    258 
    259     return data;
    260 }
    261 
    262 STATIC int
    263 tsce_sim_init(phymod_sim_data_t *pms_data,
    264                int num_entries, phymod_sim_entry_t *entries)
    265 {
    266     if (pms_data != NULL) {
    267         PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data));
    268         pms_data->num_entries = num_entries;
    269         pms_data->entries = entries;
    270     }
    271     return PHYMOD_E_NONE;
    272 }
    273 
    274 STATIC int
    275 tsce_sim_reset(phymod_sim_data_t *pms_data)
    276 {
    277     uint32_t sim_size;
    278 
    279     if (pms_data == NULL || pms_data->entries == NULL) {
    280         return PHYMOD_E_INIT;
    281     }
    282 
    283     pms_data->entries_used = 0;
    284     sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t);
    285     PHYMOD_MEMSET(pms_data->entries, 0, sim_size);
    286 
    287     return PHYMOD_E_NONE;
    288 }
    289 
    290 STATIC int
    291 tsce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data)
    292 {
    293     int idx;
    294     uint32_t aer, blk, devad, reg, copies;
    295     uint32_t lane = 0, pllidx = 0;
    296     phymod_sim_entry_t *pse;
    297 
    298     if (pms_data == NULL || pms_data->entries == NULL) {
    299         return PHYMOD_E_INIT;
    300     }
    301 
    302     devad = 0;
    303 
    304     if (addr < TSCE_BLK) {
    305         /* Assume clause 22 access */
    306         (void)tsce_sim_read(pms_data, TSCE_BLK, &blk);
    307         /* IEEE bit */
    308         if (addr & 0x10) {
    309             blk |= 0x8000;
    310         } else {
    311             blk &= ~0x8000;
    312         }
    313         addr = (addr & 0xf) | (blk & 0xfff0);
    314         if (addr != TSCE_AER && addr != TSCE_BLK) {
    315             (void)tsce_sim_read(pms_data, TSCE_AER, &aer);
    316             addr |= (aer << 16);
    317         }
    318     } else {
    319         /* Extract devad if clause 45 address format */
    320         if ((addr & TSCE_CL45_MASK) == TSCE_CL45) {
    321             devad = (addr >> 16) & 0x1f;
    322             addr &= 0xffff;
    323         }
    324     }
    325 
    326     if (addr != TSCE_AER && addr != TSCE_BLK) {
    327         /* Assume AER is in upper 16 bits */
    328         aer = (addr >> 16);
    329         if (aer == 0) {
    330             /* Try reading real AER instead */
    331             (void)tsce_sim_read(pms_data, TSCE_AER, &aer);
    332         }
    333         /* Add clause 45 devad (if used) */
    334         if (devad) {
    335             aer |= (devad << 11);
    336             addr = (addr & 0xffff) | (aer << 16);
    337         }
    338         lane = (aer & 0x7);
    339         if (lane > 3) {
    340             /* Force lane 0 if lane is invalid */
    341             addr = TSCE_ADDR(TSCE_DEVAD_GET(addr), 0, TSCE_REG_GET(addr), TSCE_PLLIDX_GET(addr));
    342         }
    343     }
    344 
    345     /* Adjust lane according to number of copies */
    346     devad = TSCE_DEVAD_GET(addr);
    347     reg = TSCE_REG_GET(addr);
    348     pllidx = TSCE_PLLIDX_GET(addr);
    349     copies = tsce_sim_reg_copies_get(addr);
    350     if (copies == 1) {
    351         lane = 0;
    352     } else if (copies == 2) {
    353         lane &= ~0x1;
    354     }
    355     addr = TSCE_ADDR(devad, lane, reg, pllidx);
    356 
    357     /* for DSC_UC_CTRL always succeed */
    358     if(addr == TSCE_SIM_DSC_UC_CTRL_REG_ADDR) {
    359         *data = 0x80;
    360         DBG_VERB(("tsce_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    361                       addr, *data));
    362         return PHYMOD_E_NONE;
    363     }
    364 
    365     /* Check if this register has been written already */
    366     for (idx = 0; idx < pms_data->entries_used; idx++) {
    367         pse = &pms_data->entries[idx];
    368         if (pse->addr == addr) {
    369             *data = pse->data;
    370             DBG_VERB(("tsce_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    371                       addr, *data));
    372             return PHYMOD_E_NONE;
    373         }
    374     }
    375 
    376     /* Return default value if register was never written */
    377     *data = tsce_sim_default_data_get(addr);
    378 
    379     DBG_VERB(("tsce_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n",
    380               addr, *data));
    381 
    382     return PHYMOD_E_NONE;
    383 }
    384 
    385 STATIC int
    386 tsce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    387 {
    388     uint32_t aer, blk, devad, reg, copies;
    389     uint32_t lane = 0, pllidx = 0;
    390 
    391     if (pms_data == NULL || pms_data->entries == NULL) {
    392         return PHYMOD_E_INIT;
    393     }
    394 
    395     devad = 0;
    396 
    397     if (addr < TSCE_BLK) {
    398         /* Assume clause 22 access */
    399         (void)tsce_sim_read(pms_data, TSCE_BLK, &blk);
    400         /* IEEE bit */
    401         if (addr & 0x10) {
    402             blk |= 0x8000;
    403         } else {
    404             blk &= ~0x8000;
    405         }
    406         addr = (addr & 0xf) | (blk & 0xfff0);
    407         if (addr != TSCE_AER && addr != TSCE_BLK) {
    408             (void)tsce_sim_read(pms_data, TSCE_AER, &aer);
    409             addr |= (aer << 16);
    410         }
    411     } else {
    412         /* Extract devad if clause 45 address format */
    413         if ((addr & TSCE_CL45_MASK) == TSCE_CL45) {
    414             devad = (addr >> 16) & 0x1f;
    415             addr &= 0xffff;
    416         }
    417     }
    418 
    419     if (addr != TSCE_AER && addr != TSCE_BLK) {
    420         /* Assume AER is in upper 16 bits */
    421         aer = (addr >> 16);
    422         if (aer == 0) {
    423             /* Try reading real AER instead */
    424             (void)tsce_sim_read(pms_data, TSCE_AER, &aer);
    425         }
    426         /* Add clause 45 devad (if used) */
    427         if (devad) {
    428             aer |= (devad << 11);
    429             addr = (addr & 0xffff) | (aer << 16);
    430         }
    431         lane = (aer & 0x7);
    432         if (lane > 6) {
    433             return PHYMOD_E_PARAM;
    434         }
    435         if (lane > 3) {
    436             /*
    437              * Handle lane broadcast
    438              *
    439              * Note that we use lane 8 instead of lane 0 to prevent a
    440              * broadcast loop. The value 8 will become 0 when masked
    441              * with 0x7, but it prevents the AER in the upper 16 bits
    442              * from being zero, which will cause the code above to
    443              * obtain the AER value from register 0xffde.
    444              */
    445             reg = TSCE_REG_GET(addr);
    446             devad = TSCE_DEVAD_GET(addr);
    447             pllidx = TSCE_PLLIDX_GET(addr);
    448             if (lane == 4 || lane == 6) {
    449                 /* Write lanes 0 and 1 */
    450                 addr = TSCE_ADDR(devad, 8, reg, pllidx);
    451                 (void)tsce_sim_write(pms_data, addr, data);
    452                 addr = TSCE_ADDR(devad, 1, reg, pllidx);
    453                 (void)tsce_sim_write(pms_data, addr, data);
    454             }
    455             if (lane == 5 || lane == 6) {
    456                 /* Write lanes 2 and 3 */
    457                 addr = TSCE_ADDR(devad, 2, reg, pllidx);
    458                 (void)tsce_sim_write(pms_data, addr, data);
    459                 addr = TSCE_ADDR(devad, 3, reg, pllidx);
    460                 (void)tsce_sim_write(pms_data, addr, data);
    461             }
    462             return PHYMOD_E_NONE;
    463         }
    464     }
    465 
    466     /* Adjust data and/or related registers */
    467     data = tsce_sim_write_adjust(pms_data, addr, data);
    468 
    469     /* Adjust lane according to number of copies */
    470     devad = TSCE_DEVAD_GET(addr);
    471     reg = TSCE_REG_GET(addr);
    472     copies = tsce_sim_reg_copies_get(addr);
    473     if (copies == 1) {
    474         lane = 0;
    475     } else if (copies == 2) {
    476         lane &= ~0x1;
    477     }
    478     pllidx = TSCE_PLLIDX_GET(addr);
    479     addr = TSCE_ADDR(devad, lane, reg, pllidx);
    480 
    481     return _tsce_sim_write(pms_data, addr, data);
    482 }
    483 
    484 STATIC int
    485 tsce_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event)
    486 {
    487     if (pms_data == NULL || pms_data->entries == NULL) {
    488         return PHYMOD_E_INIT;
    489     }
    490 
    491     return PHYMOD_E_NONE;
    492 }
    493 
    494 phymod_sim_drv_t tsce_sim_drv = {
    495     tsce_sim_init,
    496     tsce_sim_reset,
    497     tsce_sim_read,
    498     tsce_sim_write,
    499     tsce_sim_event
    500 };
    501