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

tsce16_sim.c (16699B)


      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/Merlin16 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_tsce16_xgxs_defs.h>
     42 
     43 /* Convenience macro */
     44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE
     45 
     46 /* Bit field get/set macros */
     47 #define TSCE16_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift))
     48 #define TSCE16_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 TSCE16_DEVAD_SHIFT       27
     55 #define TSCE16_DEVAD_MASK        0x1f
     56 #define TSCE16_DEVAD_GET(_addr) \
     57     TSCE16_BF_GET(_addr, TSCE16_DEVAD_MASK, TSCE16_DEVAD_SHIFT)
     58 #define TSCE16_LANE_SHIFT        16
     59 #define TSCE16_LANE_MASK         0x7
     60 #define TSCE16_LANE_GET(_addr) \
     61     TSCE16_BF_GET(_addr, TSCE16_LANE_MASK, TSCE16_LANE_SHIFT)
     62 #define TSCE16_REG_SHIFT         0
     63 #define TSCE16_REG_MASK          0xffff
     64 #define TSCE16_REG_GET(_addr) \
     65     TSCE16_BF_GET(_addr, TSCE16_REG_MASK, TSCE16_REG_SHIFT)
     66 
     67 #define TSCE16_ADDR(_devad, _lane, _reg) \
     68     (((_devad) << TSCE16_DEVAD_SHIFT) +  \
     69      ((_lane) << TSCE16_LANE_SHIFT) +    \
     70      ((_reg) << TSCE16_REG_SHIFT))
     71 
     72 #define TSCE16_AER               TSCE16_ADDR(0, 0, 0xffde)
     73 #define TSCE16_BLK               TSCE16_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 TSCE16_CL45              (0x20 << 16)
     80 #define TSCE16_CL45_MASK         (0xe0 << 16)
     81 
     82 #define TSCE16_ID0               0x600d
     83 #define TSCE16_ID1               0x8770
     84 #define TSCE16_SERDES_ID         0x0312
     85 
     86 #define TSCE16_SIM_FW_LOAD_DONE_REG_ADDR (0x0800d205)
     87 #define TSCE16_SIM_DSC_UC_CTRL_REG_ADDR (0x0800d00d)
     88 
     89 /* Forward declarations */
     90 STATIC int
     91 tsce16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data);
     92 STATIC int
     93 tsce16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data);
     94 
     95 STATIC uint32_t
     96 tsce16_sim_default_data_get(uint32_t addr)
     97 {
     98     uint32_t reg;
     99 
    100     reg = TSCE16_REG_GET(addr);
    101 
    102     /*force FW load success */
    103     if(addr == TSCE16_SIM_FW_LOAD_DONE_REG_ADDR) {
    104         return (1 << 15);
    105     }
    106 
    107     switch (reg) {
    108         case 0x0002:
    109             return TSCE16_ID0;
    110         case 0x0003:
    111             return TSCE16_ID1;
    112         case 0x900e:
    113             return TSCE16_SERDES_ID;
    114         case 0x9003:
    115             return 0xe4e4;
    116         case 0x925a:
    117             return 0x0002;
    118         case 0x925b:
    119             return 0x006b;
    120         case 0x925d:
    121             return 0x3b5f;
    122         case 0x925e:
    123             return 0x006b;
    124         case 0xa000:
    125             return 0xfffc;
    126         case 0xa001:
    127             return 0x8030;
    128         case 0xc070:
    129             return 0xff00;
    130         case 0xc111:
    131             return 0x0800;
    132         case 0xc113:
    133             return 0x01c8;
    134         case 0xc134:
    135             return 0x2870;
    136         case 0xc185:
    137             return 0x02a0;
    138         case 0xc330:
    139             return 0x0002;
    140         case 0xd089:
    141             return 0x0007;
    142         case 0xd0a2:
    143             return 0x5250;
    144         case 0xd0b1:
    145             return 0x0007;
    146         case 0xd0b4:
    147             return 0x0500;
    148         case 0xd0b8:
    149             return 0x4042;
    150         case 0xd0c1:
    151             return 0x0008;
    152         case 0xd0d2:
    153             return 0x000e;
    154         case 0xd0e2:
    155             return 0x0002;
    156         case 0xd0f4:
    157             /* this is not the default value. value is changed to pass through
    158              * core int.
    159              */
    160             return 0x8271;
    161         case 0xd0f8:
    162             return 0x0007;
    163         case 0xd111:
    164             return 0x0020;
    165         case 0xd189:
    166             return 0x0007;
    167         case 0xd203:
    168             /* this is not the default value. value is changed to pass through
    169              * core init.
    170              */
    171             return 0x1;
    172         default:
    173             return 0;
    174     }
    175     return 0;
    176 }
    177 
    178 STATIC uint32_t
    179 tsce16_sim_reg_copies_get(uint32_t addr)
    180 {
    181     uint32_t devad, reg;
    182 
    183     devad = TSCE16_DEVAD_GET(addr);
    184     reg = TSCE16_REG_GET(addr);
    185 
    186     if (reg == TSCE16_AER || reg == TSCE16_BLK) {
    187         return 1;
    188     }
    189 
    190     if (devad == 0) {
    191         if ((reg & 0xf000) == 0x9000) {
    192             return 1;
    193         }
    194         if ((reg & 0xf000) == 0xa000) {
    195             return 2;
    196         }
    197         return 4;
    198     } else if (devad == 1) {
    199         return 4;
    200     }
    201     return 0;
    202 }
    203 
    204 STATIC int
    205 _tsce16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    206 {
    207     int idx;
    208     uint32_t mask;
    209 
    210     phymod_sim_entry_t *pse;
    211 
    212     if (pms_data == NULL || pms_data->entries == NULL) {
    213         return PHYMOD_E_INIT;
    214     }
    215 
    216     /* Support optional write mask in upper 16 bits */
    217     mask = (data >> 16);
    218     if (mask == 0) {
    219         mask = 0xffff;
    220     }
    221     data &= mask;
    222 
    223     /* Check if this register has been written already */
    224     for (idx = 0; idx < pms_data->entries_used; idx++) {
    225         pse = &pms_data->entries[idx];
    226         if (pse->addr == addr) {
    227             pse->data &= ~mask;
    228             pse->data |= data;
    229             DBG_VERB(("_tsce16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    230                       addr, pse->data));
    231             return PHYMOD_E_NONE;
    232         }
    233     }
    234 
    235     /* Check if database is full */
    236     if (pms_data->entries_used >= pms_data->num_entries) {
    237         return PHYMOD_E_RESOURCE;
    238     }
    239 
    240     /* Check if new data matches default value */
    241     if (data == tsce16_sim_default_data_get(addr)) {
    242         return PHYMOD_E_NONE;
    243     }
    244 
    245     /* Add new register value */
    246     pse = &pms_data->entries[pms_data->entries_used++];
    247     pse->addr = addr;
    248     pse->data = data;
    249 
    250     DBG_VERB(("_tsce16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n",
    251               addr, pse->data));
    252 
    253     return PHYMOD_E_NONE;
    254 }
    255 
    256 STATIC uint32_t
    257 tsce16_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    258 {
    259     uint32_t devad, reg, val;
    260 
    261     devad = TSCE16_DEVAD_GET(addr);
    262     reg = TSCE16_REG_GET(addr);
    263 
    264     if (devad == 0) {
    265         switch (reg) {
    266         case 0xc050:
    267             /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */
    268             tsce16_sim_write(pms_data, addr + 1, 0x3);
    269             /* Force link up in RX_X4_PCS_LIVE_STS register
    270                offset 0x104 = 0xc154 - 0xc050 */
    271             tsce16_sim_write(pms_data, addr + 0x104, 0x1b);
    272             /* Sync configured speed into final speed status register 0x20 = 0xc070 - 0xc050 */
    273             tsce16_sim_read(pms_data, addr + 0x20, &val);
    274             val = (val & 0xffff00ff) | ((data & 0xff)<<8);
    275             tsce16_sim_write(pms_data, addr + 0x20, val);
    276             break;
    277         case 0xc055:
    278             /* Sync configured Lane Number into Lane Number status register 0x1B = 0xc070 - 0xc055 */
    279             tsce16_sim_read(pms_data, addr + 0x1B, &val);
    280             val = (val & 0xfffffff8) | (data & 0x7);
    281             tsce16_sim_write(pms_data, addr + 0x1B, val);
    282             break;
    283         default:
    284             break;
    285         }
    286     } else if (devad == 1) {
    287         switch (reg) {
    288         case 0xd127:
    289             /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */
    290             /* Write lanes 0, 1, 2 and 3 */
    291             addr = TSCE16_ADDR(devad, 0, reg);
    292             (void)_tsce16_sim_write(pms_data, addr, data);
    293 
    294             addr = TSCE16_ADDR(devad, 1, reg);
    295             (void)_tsce16_sim_write(pms_data, addr, data);
    296 
    297             addr = TSCE16_ADDR(devad, 2, reg);
    298             (void)_tsce16_sim_write(pms_data, addr, data);
    299 
    300             addr = TSCE16_ADDR(devad, 3, reg);
    301             (void)_tsce16_sim_write(pms_data, addr, data);
    302             break;
    303         case 0xd203:
    304             /* write data register to UC_RAM_WRDATA(0xd203)
    305              * read data register from UC_RAM_RDDATA(0xd204)
    306              */
    307             _tsce16_sim_write(pms_data, addr + 1, data);
    308             break;
    309         case 0xd0c1:
    310             /* Sync configured SIGDET_CTL1 register. When a port is set disable,
    311              * the port's RX_X4_PCS_LIVE_STS register deletes link status.
    312              * RX_X4_PCS_LIVE_STS register is PCS register, so need set devad to 0.
    313              * 0xF6D = 0xd0c1 - 0xc154*/
    314             tsce16_sim_read(pms_data, (addr - 0xF6D) & 0x7ffff, &val);
    315             val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2);
    316             tsce16_sim_write(pms_data, (addr - 0xF6D) & 0x7ffff, val);
    317             break;
    318         default:
    319             break;
    320         }
    321     }
    322 
    323     return data;
    324 }
    325 
    326 STATIC int
    327 tsce16_sim_init(phymod_sim_data_t *pms_data,
    328                int num_entries, phymod_sim_entry_t *entries)
    329 {
    330     if (pms_data != NULL) {
    331         PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data));
    332         pms_data->num_entries = num_entries;
    333         pms_data->entries = entries;
    334     }
    335     return PHYMOD_E_NONE;
    336 }
    337 
    338 STATIC int
    339 tsce16_sim_reset(phymod_sim_data_t *pms_data)
    340 {
    341     uint32_t sim_size;
    342 
    343     if (pms_data == NULL || pms_data->entries == NULL) {
    344         return PHYMOD_E_INIT;
    345     }
    346 
    347     pms_data->entries_used = 0;
    348     sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t);
    349     PHYMOD_MEMSET(pms_data->entries, 0, sim_size);
    350 
    351     return PHYMOD_E_NONE;
    352 }
    353 
    354 STATIC int
    355 tsce16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data)
    356 {
    357     int idx;
    358     uint32_t aer, blk, devad, reg, copies;
    359     uint32_t lane = 0;
    360     phymod_sim_entry_t *pse;
    361 
    362     if (pms_data == NULL || pms_data->entries == NULL) {
    363         return PHYMOD_E_INIT;
    364     }
    365 
    366     devad = 0;
    367 
    368     if (addr < TSCE16_BLK) {
    369         /* Assume clause 22 access */
    370         (void)tsce16_sim_read(pms_data, TSCE16_BLK, &blk);
    371         /* IEEE bit */
    372         if (addr & 0x10) {
    373             blk |= 0x8000;
    374         } else {
    375             blk &= ~0x8000;
    376         }
    377         addr = (addr & 0xf) | (blk & 0xfff0);
    378         if (addr != TSCE16_AER && addr != TSCE16_BLK) {
    379             (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer);
    380             addr |= (aer << 16);
    381         }
    382     } else {
    383         /* Extract devad if clause 45 address format */
    384         if ((addr & TSCE16_CL45_MASK) == TSCE16_CL45) {
    385             devad = (addr >> 16) & 0x1f;
    386             addr &= 0xffff;
    387         }
    388     }
    389 
    390     if (addr != TSCE16_AER && addr != TSCE16_BLK) {
    391         /* Assume AER is in upper 16 bits */
    392         aer = (addr >> 16);
    393         if (aer == 0) {
    394             /* Try reading real AER instead */
    395             (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer);
    396         }
    397         /* Add clause 45 devad (if used) */
    398         if (devad) {
    399             aer |= (devad << 11);
    400             addr = (addr & 0xffff) | (aer << 16);
    401         }
    402         lane = (aer & 0x7);
    403         if (lane > 3) {
    404             /* Force lane 0 if lane is invalid */
    405             addr = TSCE16_ADDR(TSCE16_DEVAD_GET(addr), 0, TSCE16_REG_GET(addr));
    406         }
    407     }
    408 
    409     /* Adjust lane according to number of copies */
    410     devad = TSCE16_DEVAD_GET(addr);
    411     reg = TSCE16_REG_GET(addr);
    412     copies = tsce16_sim_reg_copies_get(addr);
    413     if (copies == 1) {
    414         lane = 0;
    415     } else if (copies == 2) {
    416         lane &= ~0x1;
    417     }
    418     addr = TSCE16_ADDR(devad, lane, reg);
    419 
    420     /* for DSC_UC_CTRL always succeed */
    421     if(addr == TSCE16_SIM_DSC_UC_CTRL_REG_ADDR) {
    422         *data = 0x80;
    423         DBG_VERB(("tsce16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    424                       addr, *data));
    425         return PHYMOD_E_NONE;
    426     }
    427 
    428     /* Check if this register has been written already */
    429     for (idx = 0; idx < pms_data->entries_used; idx++) {
    430         pse = &pms_data->entries[idx];
    431         if (pse->addr == addr) {
    432             *data = pse->data;
    433             DBG_VERB(("tsce16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n",
    434                       addr, *data));
    435             return PHYMOD_E_NONE;
    436         }
    437     }
    438 
    439     /* Return default value if register was never written */
    440     *data = tsce16_sim_default_data_get(addr);
    441 
    442     DBG_VERB(("tsce16_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n",
    443               addr, *data));
    444 
    445     return PHYMOD_E_NONE;
    446 }
    447 
    448 STATIC int
    449 tsce16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data)
    450 {
    451     uint32_t aer, blk, devad, reg, copies;
    452     uint32_t lane = 0;
    453 
    454     if (pms_data == NULL || pms_data->entries == NULL) {
    455         return PHYMOD_E_INIT;
    456     }
    457 
    458     devad = 0;
    459 
    460     if (addr < TSCE16_BLK) {
    461         /* Assume clause 22 access */
    462         (void)tsce16_sim_read(pms_data, TSCE16_BLK, &blk);
    463         /* IEEE bit */
    464         if (addr & 0x10) {
    465             blk |= 0x8000;
    466         } else {
    467             blk &= ~0x8000;
    468         }
    469         addr = (addr & 0xf) | (blk & 0xfff0);
    470         if (addr != TSCE16_AER && addr != TSCE16_BLK) {
    471             (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer);
    472             addr |= (aer << 16);
    473         }
    474     } else {
    475         /* Extract devad if clause 45 address format */
    476         if ((addr & TSCE16_CL45_MASK) == TSCE16_CL45) {
    477             devad = (addr >> 16) & 0x1f;
    478             addr &= 0xffff;
    479         }
    480     }
    481 
    482     if (addr != TSCE16_AER && addr != TSCE16_BLK) {
    483         /* Assume AER is in upper 16 bits */
    484         aer = (addr >> 16);
    485         if (aer == 0) {
    486             /* Try reading real AER instead */
    487             (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer);
    488         }
    489         /* Add clause 45 devad (if used) */
    490         if (devad) {
    491             aer |= (devad << 11);
    492             addr = (addr & 0xffff) | (aer << 16);
    493         }
    494         lane = (aer & 0x7);
    495         if (lane > 6) {
    496             return PHYMOD_E_PARAM;
    497         }
    498         if (lane > 3) {
    499             /*
    500              * Handle lane broadcast
    501              *
    502              * Note that we use lane 8 instead of lane 0 to prevent a
    503              * broadcast loop. The value 8 will become 0 when masked
    504              * with 0x7, but it prevents the AER in the upper 16 bits
    505              * from being zero, which will cause the code above to
    506              * obtain the AER value from register 0xffde.
    507              */
    508             reg = TSCE16_REG_GET(addr);
    509             devad = TSCE16_DEVAD_GET(addr);
    510             if (lane == 4 || lane == 6) {
    511                 /* Write lanes 0 and 1 */
    512                 addr = TSCE16_ADDR(devad, 8, reg);
    513                 (void)tsce16_sim_write(pms_data, addr, data);
    514                 addr = TSCE16_ADDR(devad, 1, reg);
    515                 (void)tsce16_sim_write(pms_data, addr, data);
    516             }
    517             if (lane == 5 || lane == 6) {
    518                 /* Write lanes 2 and 3 */
    519                 addr = TSCE16_ADDR(devad, 2, reg);
    520                 (void)tsce16_sim_write(pms_data, addr, data);
    521                 addr = TSCE16_ADDR(devad, 3, reg);
    522                 (void)tsce16_sim_write(pms_data, addr, data);
    523             }
    524             return PHYMOD_E_NONE;
    525         }
    526     }
    527 
    528     /* Adjust data and/or related registers */
    529     data = tsce16_sim_write_adjust(pms_data, addr, data);
    530 
    531     /* Adjust lane according to number of copies */
    532     devad = TSCE16_DEVAD_GET(addr);
    533     reg = TSCE16_REG_GET(addr);
    534     copies = tsce16_sim_reg_copies_get(addr);
    535     if (copies == 1) {
    536         lane = 0;
    537     } else if (copies == 2) {
    538         lane &= ~0x1;
    539     }
    540     addr = TSCE16_ADDR(devad, lane, reg);
    541 
    542     return _tsce16_sim_write(pms_data, addr, data);
    543 }
    544 
    545 STATIC int
    546 tsce16_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event)
    547 {
    548     if (pms_data == NULL || pms_data->entries == NULL) {
    549         return PHYMOD_E_INIT;
    550     }
    551 
    552     return PHYMOD_E_NONE;
    553 }
    554 
    555 phymod_sim_drv_t tsce16_sim_drv = {
    556     tsce16_sim_init,
    557     tsce16_sim_reset,
    558     tsce16_sim_read,
    559     tsce16_sim_write,
    560     tsce16_sim_event
    561 };
    562