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_dpll_sim.c (16851B)


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