/* Copyright (c) 2017 Broadcom Corporation All Rights Reserved <:label-BRCM:2017:DUAL/GPL:standard Unless you and Broadcom execute a separate written software license agreement governing use of this software, this software is licensed to you under the terms of the GNU General Public License version 2 (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, with the following added to such license: As a special exception, the copyright holders of this software give you permission to link this software with independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from this software. The special exception does not apply to any modifications of the software. Not withstanding the above, under no circumstances may you combine this software in any way with any other Broadcom software provided under a license other than the GPL, without Broadcom's express prior written consent. :> */ /* * Created on: Sep 2017 * Author: li.xu@broadcom.com */ /* * Phy drivers for 10G Active Ethernet Serdes */ #include "phy_drv_dsl_serdes.h" #include "bcm_physical_map_part.h" #include "phy_drv_merlin28.h" #include "M2_merlin.h" static int log_level = 2; #define MAX_LANES_PER_CORE 2 #if 0 #define print_log(fmt, args...) do { \ LANE=LANE; CoreNum=CoreNum; printk(fmt, ##args); \ } while(0) #else #define print_log(fmt, args...) do { \ phy_serdes_t *phy_serdes = phy_drv_serdes6756_get_serdes(CoreNum, LANE); \ if (log_level >= 2 && phy_serdes->inited < 3) printk(fmt, ##args); \ } while(0) #endif #define print_log2(fmt, args...) do { \ if (log_level >= 2) printk(fmt, ##args); \ } while(0) // #define FATAL_DEBUG #if !defined(FATAL_DEBUG) #define fatal_log(fmt, args...) do { \ int static init = 0; \ printk(ErrClr "******* %s:%s:%d: FATAL ERROR: ", __FILE__,__func__,__LINE__); \ printk(KERN_CONT fmt DflClr, ##args); while(0) {msleep(3000);} \ if (init < 0) \ init++; \ else \ BUG(); \ } while(0) #else #define fatal_log(fmt, args...) do { \ int static init = 0; \ if (++init <= 2) { \ printk(ErrClr "******* %s:%s:%d: FATAL ERROR: ", __FILE__,__func__,__LINE__); \ printk(KERN_CONT fmt DflClr, ##args); \ printk(WrnClr); WARN_ON(1); printk(DflClr); \ } \ } while(0) #endif // #define FATAL_DEBUG #define error_log(fmt, args...) do {if (log_level >= 1) printk("*** %s:%d: Waring:", __func__,__LINE__); printk(KERN_CONT fmt, ##args);} while(0) #include "merlin28_shortfin_ucode_image.h" #if 0 #include "merlin28_mdio.h" #define set_mask_read_data(d) udelay(1000) #define clr_mask_read_data() udelay(1000) #endif enum {VCO_NOT_SET, VCO_9P375G, VCO_10G}; static void merlin28_reg_prog(prog_seq_tbl *prog_seq_tbl_ptr, uint32 CoreNum, uint32 LANE); static int parse_sim_opts(char *str) { int i; static struct ss {char *s; int ret;} opt[64] = { {"-d VERBOSE", 1}, {"-d ML_C_ALLOW_PMI_TIMEOUT", 0}, {"-d MERLIN_BC", 0}, {"-d ML_EXT_REFCLK", 0}, {"-d MERLIN_LOAD_FIRMWARE", 1}, {"-d MERLIN_UC_VERIFY_CRC", 1}, {"-d USE_MDIO", 0}, {"-d ML_REFCLK_50", 1}, {"-d separate_vco", 1}, }; for (i=0; i= ARRAY_SIZE(opt)) fatal_log("Error:Option array overflow: %s\n", str); else { opt[i].s = str; opt[i].ret = 3; print_log2("No option found for: \"%s\", added default value 0 in index %d\n", str, i); } return 0; } #if 0 int logger_write(int message_verbose_level, const char *format, ...) { va_list ap; int len; va_start(ap, format); printk("level: %d;", message_verbose_level); len = vprintk(format, ap); va_end(ap); return len; } /** Read a register from the currently selected Serdes IP Lane. * @param sa__ is an opaque state vector passed through to device access functions. * @param address Address of register to be read * @param *val value read out from the register * @return Error code generated by read function (returns ERR_CODE_NONE if no errors) */ err_code_t merlin28_shortfin_pmd_rdt_reg(srds_access_t *sa__, uint16_t address, uint16_t *val) { *val = merlin28_pmi_read16_delay(sa__->core_num, 0, PMD_DEV, address, 0); return ERR_CODE_NONE; } err_code_t merlin28_shortfin_pmd_mwr_reg(srds_access_t *sa__, uint16_t addr, uint16_t mask, uint8_t lsb, uint16_t val) /* mask: 1:Write; Reversed to hardware */ { return merlin28_pmi_write16_delay(sa__->core_num, 0, PMD_DEV, addr, (val<> 12) < 8 ? (DEV_ADDR << 12) | (REG_ADDR & 0xfff) : REG_ADDR; acc_addr_data = ((DEV_ADDR & 0x1f) << 27) | //[31:27]: device address ((LANE & 0x7ff) << 16) | //[26:16]: lane address (reg_addr & 0xffff); //[15:0]: register address // acc_addr_data = ((DEV_ADDR & 0x1f) << 27) | //[31:27]: device address // ((LANE & 0x7ff) << 16) | //[26:16]: lane address // (REG_ADDR & 0xffff); //[15:0]: register address acc_mask_data = (MASK & 0xffff); //[15:0]: reg_mask; writting to the corresponding data bit is disabled. if (delay_acked) delayed_ack = 0x1; else delayed_ack = 0x0; acc_ctrl_data = (delayed_ack << 18) | //[18]: delayed_ack (0x1 << 17) | //[17]: start_busy. (0x0 << 16) | //[16]: r_w Register transaction. 0: Write; 1: Read (DATA & 0xffff); //[15:0]: reg_data if(0 && parse_sim_opts("-d VERBOSE")) { print_log("... %s():Writing acc_addr_data 0x%08x at acc_addr_addr 0x%08x\n", __func__, acc_addr_data, acc_addr_addr); print_log("... %s():Writing acc_mask_data 0x%08x at acc_mask_addr 0x%08x\n", __func__, acc_mask_data, acc_mask_addr); print_log("... %s():Writing acc_ctrl_data 0x%08x at acc_ctrl_addr 0x%08x\n", __func__, acc_ctrl_data, acc_ctrl_addr); } host_reg_write(acc_addr_addr,acc_addr_data); host_reg_write(acc_mask_addr,acc_mask_data); host_reg_write(acc_ctrl_addr,acc_ctrl_data); watch_dog=0; if (delay_acked) { busy = 0x0; //false; direct } else { busy = 0x1; //true; polling // print_log("... %s(): PMI is in indirect mode\n", __func__); } while (busy) { acc_ctrl_read = host_reg_read(acc_ctrl_addr); busy = acc_ctrl_read & (0x1 << 17); //[17]: start_busy mask //timeout_ns( 100 ); //0.1us tick watch_dog = watch_dog + 1; if (watch_dog > 15) { // should be finished by 1us if(parse_sim_opts("-d ML_C_ALLOW_PMI_TIMEOUT")) { if( (REG_ADDR == 0x0000) && (DATA == 0xdead) && (MASK == 0xffff) ) { // print_log("INFO !!! %s(): PMI write has time-out as expected, simulation resumes becasue of '-d ML_C_ALLOW_PMI_TIMEOUT' is specified\n", __func__); } else { error_log("INFO !!! %s(): PMI write has time-out\n", __func__); } busy = 0x0; } else { if ((delayed_ack == 0x0) && (acc_ctrl_read == 0xeeeeeeee) && (REG_ADDR == 0xc30b)) { //rbus error handling for chip_test/r2pmi_lp_bridge_test.cc // print_log("INFO !!! %s(): PMI write has time-out as expected, simulation resumed for r2pmi_lp_bridge_test\n", __func__); busy = 0; } else { fatal_log("ERROR !!! %s():PMI transaction write-to Merlin Core #%d has timed-out\n", __func__, CoreNum); } } } else { if(0 && parse_sim_opts("-d VERBOSE")) { print_log("... %s(): acc_ctrl = 0x%x @watch dog count = 0x%08d\n", __func__, acc_ctrl_read, watch_dog); } } } if(0 && parse_sim_opts("-d VERBOSE")) { print_log("INFO %s(): PMI write done\n", __func__); }; return 0; } uint32 merlin28_pmi_read16_delay(uint32 CoreNum, uint32 LANE, uint32 DEV_ADDR, uint32 REG_ADDR, bool DELAY_ACKED) { uint32 acc_ctrl_data; uint32 acc_addr_data; uint32 acc_mask_data; uint32 busy, delayed_ack; uint32 acc_ctrl_addr; uint32 acc_addr_addr; uint32 acc_mask_addr; uint32 acc_ctrl_read; int watch_dog; acc_ctrl_data = 0; acc_addr_data = 0; acc_mask_data = 0; acc_ctrl_addr = 0; acc_addr_addr = 0; acc_mask_addr = 0; acc_ctrl_read = 0; if (CoreNum == PMI_BC_ADDRESS) { acc_ctrl_addr = (MERLIN_PMI_BC_ADDR<<8) + MERLIN_INDIR_ACC_CNTRL; acc_addr_addr = (MERLIN_PMI_BC_ADDR<<8) + MERLIN_INDIR_ACC_ADDR; acc_mask_addr = (MERLIN_PMI_BC_ADDR<<8) + MERLIN_INDIR_ACC_MASK; } else { acc_ctrl_addr = (CoreNum*0x100) + MERLIN_INDIR_ACC_CNTRL; acc_addr_addr = (CoreNum*0x100) + MERLIN_INDIR_ACC_ADDR; acc_mask_addr = (CoreNum*0x100) + MERLIN_INDIR_ACC_MASK; } acc_addr_data = ((DEV_ADDR & 0x1f) << 27) | //[31:27]: device address ((LANE & 0x7ff) << 16) | //[26:16]: Lance address (REG_ADDR & 0xffff); //[15:0]: register address acc_mask_data = 0x0; //[15:0]: reg_mask; writting to the corresponding data bit is disabled. if (DELAY_ACKED) delayed_ack = 0x1; else delayed_ack = 0x0; acc_ctrl_data = (delayed_ack << 18) | //[18]: delayed_ack (0x1 << 17) | //[17]: start_busy. (0x1 << 16) | //[16]: r_w Register transaction. 0: Write; 1: Read (0x000); //[15:0]: reg_data if(0 && parse_sim_opts("-d VERBOSE")) { print_log("... %s():Writing acc_addr_data 0x%08x at acc_addr_addr 0x%08x\n", __func__, acc_addr_data, acc_addr_addr); print_log("... %s():Writing acc_mask_data 0x%08x at acc_mask_addr 0x%08x\n", __func__, acc_mask_data, acc_mask_addr); print_log("... %s():Writing acc_ctrl_data 0x%08x at acc_ctrl_addr 0x%08x\n", __func__, acc_ctrl_data, acc_ctrl_addr); } host_reg_write(acc_addr_addr,acc_addr_data); host_reg_write(acc_mask_addr,acc_mask_data); host_reg_write(acc_ctrl_addr,acc_ctrl_data); watch_dog=0; if (DELAY_ACKED) { busy = 0x0; //false; direct } else { busy = 0x1; //true; polling // print_log("... %s(): PMI is in indirect mode\n", __func__); } while (busy) { acc_ctrl_read = host_reg_read(acc_ctrl_addr); busy = acc_ctrl_read & (0x1 << 17); //[17]: start_busy mask //timeout_ns( 100 ); //0.1us tick watch_dog = watch_dog + 1; if (watch_dog > 15) { // should be finished by 1us if(parse_sim_opts("-d ML_C_ALLOW_PMI_TIMEOUT")) { error_log("INFO !!! %s(): PMI read has time-out\n", __func__); } else { if ((delayed_ack == 0x0) && (acc_ctrl_read == 0xeeeeeeee) && (REG_ADDR == 0xc30b)) { //rbus error handling for chip_test/r2pmi_lp_bridge_test.cc // print_log("INFO !!! %s(): PMI write has time-out as expected, simulation resumed for r2pmi_lp_bridge_test\n", __func__); busy = 0; } else { fatal_log("ERROR !!! %s():PMI transaction read-from Merlin Core #%d has timed-out\n", __func__, CoreNum); } } } else { if(0 && parse_sim_opts("-d VERBOSE")) { print_log("... %s(): watch dog count = 0x%08d\n", __func__, watch_dog); } } } if (DELAY_ACKED) { acc_ctrl_read = host_reg_read(acc_ctrl_addr); } acc_ctrl_read = (acc_ctrl_read & 0xffff); return(acc_ctrl_read); } /***********************************************/ /* Microcode Load into Program RAM Functions */ /***********************************************/ static void merlin28_load_firmware (phy_dev_t *phy_dev) { bool poll; int watch_dog; uint32 pmi_rd_data; uint32_t ucode_len; uint32_t ucode_len_padded, count = 0; uint8_t wrdata_lsb; uint16_t wr_data; uint8_t result; uint32 CoreNum = phy_dev->core_index; uint32 LANE = phy_dev->lane_index; #define UCODE_MAX_SIZE 32768 //merlin28_mptwo_common.h //merlin28_pmi_write16(uint32 CoreNum, uint32 LANE, uint32 DEV_ADDR, uint32 REG_ADDR, uint16 DATA, uint16 MASK, bool DELAY_ACKED) print_log("%s(): INFO: Start to download firmware to core #%4x\n", __func__, CoreNum); //-------------- merlin16_shortfin_config.c.merlin16_shortfin_ucode_pram_load() start ------------------------- if (MERLIN_MPTWO_UCODE_IMAGE_SIZE > UCODE_MAX_SIZE) { fatal_log("%s(): ERR_CODE_INVALID_UCODE_LEN !!!\n", __func__); } //EFUN(wrc_micro_mdio_dw8051_reset_n(0x0)); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xffef); //EFUN(wrc_micro_system_clk_en(0x1)); /* Enable clock to micro */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd20d, 0x0001, 0xfffe); //EFUN(wrc_micro_system_reset_n(0x1)); /* De-assert reset to micro */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd20d, 0x0002, 0xfffd); //EFUN(wrc_micro_system_reset_n(0x0)); /* Assert reset to micro - Toggling micro reset*/ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd20d, 0x0000, 0xfffd); //EFUN(wrc_micro_system_reset_n(0x1)); /* De-assert reset to micro */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd20d, 0x0002, 0xfffd); //EFUN(wrc_micro_mdio_ram_access_mode(0x0)); /* Select Program Memory access mode - Program RAM load when Serdes DW8051 in reset */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfe7f); //EFUN(wrc_micro_byte_mode(0)); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfdff); //EFUN(wrc_micro_ram_address(0x0)); /* RAM start address */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd201, 0x0000, 0x0000); if (1) { //addition to the ucode_pram_load() if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x0); //disable BC bridge pmi_rd_data = merlin28_pmi_read16(0x0, 0x0, 0x1, 0xd205); host_reg_write(MERLIN_PMI_BC_CNTRL,0x1<<8); //re-enable BC bridge } else { pmi_rd_data = merlin28_pmi_read16(CoreNum, 0x0, 0x1, 0xd205); } if ((pmi_rd_data & 0x8000) == 0x0000) { //[15]:micro_init_done print_log("%s(): Checking init_done initial value passed...\n", __func__); } else { fatal_log("%s(): Checking init_done initial value failed !!!\n", __func__); } } //EFUN(wrc_micro_init_cmd(0x0)); /* Clear initialization command */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0x7fff); //EFUN(wrc_micro_init_cmd(0x1)); /* Set initialization command */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x8000, 0x7fff); //EFUN(wrc_micro_init_cmd(0x0)); /* Clear initialization command */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0x7fff); //EFUN(merlin28_mptwo_delay_us(300)); /* Wait for Initialization to finish */ //timeout_ns(300000); //300us timeout_ns(250000); //250us; observed ~210us in waveform /* Poll for micro_ra_initdone = 1 to indicate initialization done */ poll = true; watch_dog = 0; if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x0); //disable BC bridge } while (poll) { if (CoreNum == PMI_BC_ADDRESS) { pmi_rd_data = merlin28_pmi_read16(0x0, 0x0, 0x1, 0xd205); } else { pmi_rd_data = merlin28_pmi_read16(CoreNum, 0x0, 0x1, 0xd205); } if ((pmi_rd_data & 0x8000) == 0x8000) { //[15]:micro_init_done print_log("%s(): Checking init_done passed ...\n", __func__); poll = false; } else { watch_dog++; if (watch_dog > 100) { //100ms fatal_log("%s(): Checking init_done failed !!! - pmi_rd_data: %04x\n", __func__, pmi_rd_data); } } timeout_ns(1000000); } if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x1<<8); //re-enable BC bridge } ucode_len = MERLIN_MPTWO_UCODE_IMAGE_SIZE; ucode_len_padded = ((ucode_len + 7) & 0xFFF8); /* Aligning ucode size to 8-byte boundary */ /* Code to Load microcode */ //EFUN(wrc_micro_ram_count(ucode_len_padded - 1)); /* Set number of bytes of ucode to be written */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd200, (ucode_len_padded - 1), 0x0000); //EFUN(wrc_micro_ram_address(0x0)); /* Start address of Program RAM where the ucode is to be loaded */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd201, 0x0000, 0x0000); //EFUN(wrc_micro_stop(0x0)); /* Stop Write to Program RAM */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfffd); //EFUN(wrc_micro_write(0x1)); /* Enable Write to Program RAM */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0008, 0xfff7); //EFUN(wrc_micro_run(0x1)); /* Start Write to Program RAM */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0001, 0xfffe); print_log("%s(): begin writing firmware to the program ram ...\n", __func__); count = 0; do { /* ucode_image loaded 16bits at a time */ if (count % ((ucode_len_padded/80)&(~3)) == 0) print_log(KERN_CONT "#"); if ( (count%1000) == 0) { //print_log("%s(): DEBUG INFO: writing bytes >%d to program ram...\n", __func__, count); } wrdata_lsb = (count < ucode_len) ? merlin28_mptwo_ucode_image[count] : 0x0; /* wrdata_lsb read from ucode_image; zero padded to 8byte boundary */ count++; wr_data = (count < ucode_len) ? merlin28_mptwo_ucode_image[count] : 0x0; /* wrdata_msb read from ucode_image; zero padded to 8byte boundary */ count++; wr_data = ((wr_data << 8) | wrdata_lsb); /* 16bit wr_data formed from 8bit msb and lsb values read from ucode_image */ //EFUN(wrc_micro_ram_wrdata(wr_data)); /* Program RAM write data */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd203, wr_data, 0x0000); } while (count < ucode_len_padded); /* Loop repeated till entire image loaded (upto the 8byte boundary) */ //EFUN(wrc_micro_write(0x0)); /* Clear Write enable to Program RAM */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfff7); //EFUN(wrc_micro_run(0x0)); /* Clear RUN command to Program RAM */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfffe); //EFUN(wrc_micro_stop(0x1)); /* Stop Write to Program RAM */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0002, 0xfffd); if (1) { /* Verify Microcode load */ if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x0); //disable BC bridge pmi_rd_data = merlin28_pmi_read16(0x0, 0x0, 0x1, 0xd205); host_reg_write(MERLIN_PMI_BC_CNTRL,0x1<<8); //re-enable BC bridge } else { pmi_rd_data = merlin28_pmi_read16(CoreNum, 0x0, 0x1, 0xd205); } result = (pmi_rd_data & 0x0011); //[1:0]:micro_err1, micro_err0 if (result > 0) { /* Look for errors in micro load FSM */ //EFUN_PRINTF(("download status =%x\n",result)); fatal_log("%s(): download status = !!!\n", __func__); //EFUN(wrc_micro_stop(0x0)); /* Clear stop field */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfffd); } else { //EFUN(wrc_micro_stop(0x0)); /* Clear stop field */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfffd); /* EFUN(wrc_micro_mdio_dw8051_reset_n(0x1)); */ /* De-assert reset to Serdes DW8051 to start executing microcode */ } } else { //EFUN(wrc_micro_stop(0x0)); /* Clear stop field */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfffd); } print_log("%s(): end writing firmware to the program ram ...\n", __func__); //-------------- merlin16_shortfin_config.c.merlin16_shortfin_ucode_pram_load() end -------------------------- } //#include "mln_firmware.cc" static void merlin28_powerdn_lane (uint32 CoreNum, uint32 LaneNum) { int LANE = LaneNum; print_log("MerlinSupport::%s(): powering down core #0x%x lane #0x%x\n", __func__, CoreNum, LaneNum); merlin28_reg_prog(powerdn_lane, CoreNum, LaneNum); } static void merlin28_powerup_lane (uint32 CoreNum, uint32 LaneNum) { int LANE = LaneNum; print_log("MerlinSupport::%s(): powering up core #0x%x lane #0x%x\n", __func__, CoreNum, LaneNum); merlin28_reg_prog(powerup_lane, CoreNum, LaneNum); } static void merlin28_mptwo_poll_uc_dsc_ready_for_cmd_equals_1 (uint32 CoreNum, uint32 LaneNum); static void merlin28_lane_init(phy_dev_t *phy_dev) { int CoreNum = phy_dev->core_index; int LANE = phy_dev->lane_index; //[1:0] prbs_chk_en_timer_mode 2'b10: use heatbeat_toggle_1us for the timer merlin28_pmi_write16(CoreNum, phy_dev->lane_index, 0x1, 0xd0d4, 0x0002, 0xfffc); print_log("%s(): Step 4.c wait for uc_dsc_ready_for_cmd = 1 \n", __func__); // merlin28_mptwo_poll_uc_dsc_ready_for_cmd_equals_1 (CoreNum, phy_dev->lane_index); } int merlin28_lane_power_op(phy_dev_t *phy_dev, int power_level) { phy_serdes_t *phy_serdes = phy_dev->priv; if (power_level == phy_serdes->cur_power_level) return 0; phy_serdes->cur_power_level = power_level; switch(power_level) { case SERDES_POWER_UP: merlin28_powerup_lane(phy_dev->core_index, phy_dev->lane_index); msleep(10); merlin28_lane_init(phy_dev); break; case SERDES_POWER_DOWN: merlin28_powerdn_lane(phy_dev->core_index, phy_dev->lane_index); msleep(10); break; } return 0; } static int _merlin28_core_power_op(phy_dev_t *phy_dev, int power_level) { uint32 wr_data; uint32 wr_addr; int rc = 0; phy_serdes_t *phy_serdes = phy_dev->priv; phy_serdes_t *serdes_core = phy_serdes->priv; uint32 CoreNum = phy_dev->core_index; uint32 LANE = phy_dev->lane_index; int LN_OFFSET = phy_dev->lane_index; int REFSEL = 0; if (power_level == serdes_core->cur_power_level) return 0; wr_addr = (CoreNum*SERDES_REG_OFFSET) + MERLIN_CTRL; serdes_core->cur_power_level = power_level; switch(power_level) { case SERDES_POWER_UP: if (parse_sim_opts("-d ML_EXT_REFCLK")) { REFSEL = 0x3; print_log("INFO %s(): Disable Analog PLL VCO CHECK for ML_EXT_REFCLK\n", __func__); } print_log("INFO %s(): START powering up Merlin Core #%d with PRTAD = %d, ln_offset_stap = %d\n", __func__, CoreNum, phy_dev->addr, 0); print_log("INFO %s(): Disable IDDQ\n", __func__); wr_data = (phy_dev->addr << MERLIN_SERDES_CTRL_PRTAD_OFFSET) | //MDIO phy address (LN_OFFSET << MERLIN_SERDES_CTRL_LN_OFFSET_OFFSET) | // LANE address (REFSEL << MERLIN_SERDES_CTRL_REFSEL_OFFSET) | (0x1 << 23) | //delay_acked (MERLIN_SERDES_CTRL_RESET_MASK << MERLIN_SERDES_CTRL_RESET_OFFSET) | (MERLIN_SERDES_CTRL_REFCLK_RESET_MASK << MERLIN_SERDES_CTRL_REFCLK_RESET_OFFSET) | (0 << MERLIN_SERDES_CTRL_IDDQ_OFFSET); host_reg_write(wr_addr, wr_data); // Provide a delay of 1ms after IDDQ is de-asserted print_log("INFO %s(): Provide a delay of 1ms after IDDQ is de-asserted\n", __func__); msleep(10); print_log("INFO %s(): Disable serdes_reset and refclk_reset\n", __func__); // clear iso_enable, clear serdes reset, ref. clock reset wr_data &= ~((MERLIN_SERDES_CTRL_RESET_MASK << MERLIN_SERDES_CTRL_RESET_OFFSET) | (MERLIN_SERDES_CTRL_REFCLK_RESET_MASK << MERLIN_SERDES_CTRL_REFCLK_RESET_OFFSET)); /* Set Broadcast MDIO address to the same address to avoid occupying 0 address */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xffdc, phy_dev->addr, 0xffe0); host_reg_write(wr_addr, wr_data); msleep(10); break; case SERDES_POWER_DOWN: print_log("INFO %s: assert IDDQ|RESET|REFCLK_RESET to power down Serdes\n", __func__); wr_data = host_reg_read(wr_addr); /* For the first time after power on, we need to set MIDO address and LANE address */ wr_data &= ~((MERLIN_SERDES_CTRL_PRTAD_MASK << MERLIN_SERDES_CTRL_PRTAD_OFFSET)| //MDIO phy address (MERLIN_SERDES_CTRL_LN_OFFSET_MASK << MERLIN_SERDES_CTRL_LN_OFFSET_OFFSET)); // LANE address wr_data |= (phy_dev->addr << MERLIN_SERDES_CTRL_PRTAD_OFFSET)| //MDIO phy address (LN_OFFSET << MERLIN_SERDES_CTRL_LN_OFFSET_OFFSET) | // LANE address (MERLIN_SERDES_CTRL_IDDQ_MASK << MERLIN_SERDES_CTRL_IDDQ_OFFSET) | (MERLIN_SERDES_CTRL_RESET_MASK << MERLIN_SERDES_CTRL_RESET_OFFSET) | (MERLIN_SERDES_CTRL_REFCLK_RESET_MASK << MERLIN_SERDES_CTRL_REFCLK_RESET_OFFSET); host_reg_write(wr_addr, wr_data); msleep(10); break; } return rc; } static void serdes_core_reset(phy_dev_t *phy_dev) { uint32 CoreNum = phy_dev->core_index; uint32 LANE = phy_dev->lane_index; print_log("Toggle Serdes Core #%d LANE #%d PMD and uC reset.\n", CoreNum, LANE); merlin28_pmi_write16(CoreNum, 0, 0x1, 0xd0f1, 0x0000, 0x0000); msleep(1); merlin28_pmi_write16(CoreNum, 0, 0x1, 0xd0f1, 0x0001, 0x0000); msleep(1); } static void merlin28_core_init(phy_dev_t *phy_dev) { /* Core Initialization from Power down/Reset state */ uint32 CoreNum = phy_dev->core_index; uint32 LANE = phy_dev->lane_index; serdes_core_reset(phy_dev); if (parse_sim_opts("-d ML_EXT_REFCLK")) { print_log("INFO %s(): Enable Analog PLL VCO CHECK for ML_EXT_REFCLK\n", __func__); } print_log("INFO %s(): END. Core #%d with PRTAD = %d, ln_offset_stap = %d\n", __func__, CoreNum, phy_dev->addr, 0); } #if 0 static int merlin28_core_power_op(phy_dev_t *phy_dev, int power_level) { phy_serdes_t *phy_serdes = phy_dev->priv; if (power_level == phy_serdes->cur_power_level) return 0; _merlin28_core_power_op(phy_dev, power_level); if (power_level == SERDES_POWER_UP) merlin28_serdes_init(phy_dev); return 0; } #endif static void merlin28_uc_reset(uint32 CoreNum, uint8_t enable) { if (enable) { /* Assert micro reset and reset all micro registers (all non-status registers written to default value) */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD200, 0x0000, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD201, 0x0000, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD202, 0x0000, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD203, 0x0000, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD207, 0x0000, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD208, 0x0000, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD20A, 0x080f, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD20C, 0x0002, 0x0000); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xD20D, 0x0000, 0x0000); } else { /* De-assert micro reset - Start executing code */ //EFUN(wrc_micro_system_clk_en(0x1)); /* Enable clock to micro */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd20d, 0x0001, 0xfffe); //EFUN(wrc_micro_system_reset_n(0x1)); /* De-assert reset to micro */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd20d, 0x0002, 0xfffd); //EFUN(wrc_micro_mdio_dw8051_reset_n(0x1)); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0010, 0xffef); } } #if 0 static void merlin28_wait_uc_active (uint32 CoreNum) { uint32 pmi_rd_data; uint32 L_CoreNum = CoreNum; //delay print_log("%s(): wait 50us comclks for micro to be up...\n", __func__); timeout_ns (12000); //12us pmi_rd_data = merlin28_pmi_read16_delay(L_CoreNum, phy_dev->lane_index, 0x1, 0xd0f4, true); if ((pmi_rd_data & 0x8000) == 0x8000) { //uc_active@[15] print_log("%s(): Checking uc_active passed ...\n", __func__); } else { fatal_log("%s(): Checking uc_active failed !!!\n", __func__); } //addition to the 3.1 Core Initialization from Power down state timeout_ns (20000); //20us msleep(100); pmi_rd_data = merlin28_pmi_read16(L_CoreNum, phy_dev->lane_index, 0x1, 0xd00d); if ((pmi_rd_data & 0x0080) == 0x0080) { //uc_dsc_ready_for_cmd@[7] if ((pmi_rd_data & 0x0040) == 0x0040) { //bit 6 is uc_dsc_error_found fatal_log("%s(): us_dsc_ready for cmd is set and uc_dsc_error is also set !!!\n", __func__); } else { print_log("%s(): micro is ready for command ...\n", __func__); } } else { fatal_log("%s(): uc_dsc_ready_for_cmd is not set !!!\n", __func__); } if (parse_sim_opts("-d MERLIN_BC") && (CoreNum<3)) { host_reg_write(ETH_PHY_TOP_REG_R2PMI_LP_BCAST_MODE_CNTRL,0x100); } } #endif static void merlin28_mdio_cl45_wr(uint32 PHYAD, uint32 DEVAD, uint32 REG_ADDR, uint16 DATA) { BUG_CHECK("MDIO is not used, we use PMI\n"); #if 0 uint32 mdio_ctrl_data, busy, mdio_err; mdio_err = 0; host_reg_write(MDIO_CFG,0x40); //set to Clause 45 // print_log("%s(): Writing to phy_addr=0x%02x dev_addr=0x%02x reg_addr=0x%04x data=0x%04x\n", __func__, PHYAD, DEVAD, REG_ADDR, DATA); //address phase busy = 1; mdio_ctrl_data = MDIO_CTRL_START|(MDIO_CTRL_CL45_ADDRESS<entries; i++) { reg_tbl = &save_regs->regs[i]; tbl_ent = reg_tbl->ent; merlin28_pmi_write16(CoreNum, LaneNum, tbl_ent->dev_addr, tbl_ent->reg_addr, reg_tbl->val, 0); } save_regs->entries = 0; return; } #define PRINT_DUPLICATED_REG_SAVE 0 for (i = 0; i < save_regs->entries; i++) /* Check duplicated saved registers */ { reg_tbl = &save_regs->regs[i]; tbl_ent = reg_tbl->ent; if (ent->reg_addr == tbl_ent->reg_addr && ent->dev_addr == tbl_ent->dev_addr) { #if PRINT_DUPLICATED_REG_SAVE printk("Saved Entry:0x%x, %s, dev:%d, reg:0x%x, val:0x%x; New:0x%x, %s, dev:%d, reg:0x%x, val:0x%x\n", (uint32_t)tbl_ent, tbl_ent->reg_desc, tbl_ent->dev_addr, tbl_ent->reg_addr, tbl_ent->data, (uint32_t)ent, ent->reg_desc, ent->dev_addr, ent->reg_addr, ent->data); #endif return; } } i = save_regs->entries; if (++save_regs->entries >= ARRAY_SIZE(save_regs->regs)) BUG_CHECK("Error: merlin register saving array overflow\n"); reg_tbl = &save_regs->regs[i]; reg_tbl->ent = ent; reg_tbl->val = merlin28_pmi_read16(CoreNum, LaneNum, ent->dev_addr, ent->reg_addr); } #if 1 #define restore_regs_core(CoreNum, LaneNum) save_reg_single(0, CoreNum, LaneNum, &core_regs) #define restore_regs_lane(CoreNum, LaneNum) save_reg_single(0, CoreNum, LaneNum, &lane_regs[LaneNum]) #else #define restore_regs_core(CoreNum, LaneNum) printk("Restore core registers\n"); save_reg_single(0, CoreNum, LaneNum, &core_regs); printk("end of Restore core\n"); #define restore_regs_lane(CoreNum, LaneNum) printk("Restore lane registers\n"); save_reg_single(0, CoreNum, LaneNum, &lane_regs[LaneNum]); printk("end of Restore lane\n"); #endif static int save_reg_mode; #define SAVE_REG_NO 0 #define SAVE_REG_CORE 1 #define SAVE_REG_LANE 2 static inline void save_reg_fun(prog_seq_tbl *ent, int CoreNum, int LaneNum) { if (save_reg_mode == SAVE_REG_CORE) save_reg_single(ent, CoreNum, LaneNum, &core_regs); else if (save_reg_mode == SAVE_REG_LANE) save_reg_single(ent, CoreNum, LaneNum, &lane_regs[LaneNum]); } static void merlin28_reg_prog(prog_seq_tbl *prog_seq_tbl_ptr, uint32 CoreNum, uint32 LANE) { uint32 i; uint32 data_mask; uint32 iter; while(prog_seq_tbl_ptr->reg_desc[0]) { if(parse_sim_opts("-d VERBOSE")) { //print_log("%s():%s\n", __func__, prog_seq_tbl_ptr->reg_desc); } if (strcmp(prog_seq_tbl_ptr->reg_desc, "timeout_100ns") == 0) { iter = prog_seq_tbl_ptr->dev_addr & 0xffff; //print_log("%s(timeout_100ns): %d loops \n", __func__, iter); //print_log("BEGIN : \n"); for (i=0; idata_bitEn) & 0xffff; //print_log("... Writing 0x%04x with data mask of 0x%04x to core #%d lane #%d, dev address 0x%02x, register address 0x%04x\n", //prog_seq_tbl_ptr->data, data_mask, CoreNum, LANE, prog_seq_tbl_ptr->dev_addr, prog_seq_tbl_ptr->reg_addr); if(parse_sim_opts("-d USE_MDIO")) { merlin28_mdio_cl45_rmw(CoreNum, LANE, prog_seq_tbl_ptr->dev_addr, prog_seq_tbl_ptr->reg_addr, prog_seq_tbl_ptr->data, data_mask); } else { save_reg_fun(prog_seq_tbl_ptr, CoreNum, LANE); merlin28_pmi_write16(CoreNum, LANE, prog_seq_tbl_ptr->dev_addr, prog_seq_tbl_ptr->reg_addr, prog_seq_tbl_ptr->data, data_mask); } prog_seq_tbl_ptr++; } } } static void merlin28_mptwo_wrw_uc_ram (uint32 CoreNum, uint16_t addr, uint16_t wr_val) // merlin28_mptwo_dv_functions.c { //EFUN(wrc_micro_mdio_ram_access_mode(0x2)); /* Select Data RAM access through MDIO Register interface mode */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0100, 0xfe7f); //EFUN(wrc_micro_byte_mode(0x0)); /* Select Word access mode */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfdff); //EFUN(wrc_micro_ram_address(addr)); /* RAM Address to be written to */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd201, addr, 0x0000); //EFUN(merlin28_mptwo_delay_ns(80)); /* wait for 10 comclk cycles/ 80ns */ timeout_ns (80); //80ns //EFUN(wrc_micro_ram_wrdata(wr_val)); /* RAM Write value */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd203, wr_val, 0x0000); //EFUN(merlin28_mptwo_delay_ns(80)); /* Wait for Data RAM to be written */ timeout_ns (80); //80ns } static void merlin28_cfg_core_ram_var (uint32 CoreNum, uint16_t vco_rate) { uint32_t core_var_ram_base = 0x0; uint16_t an_los_workaround = 0x0<<6; uint16_t core_cfg_from_pcs = 0x1; //This is set to 1 by default when using HSIP Config Interface uint16_t core_cfg_vco_rate = 0x0; uint16_t core_config_word = 0x0; uint16_t addr = 0x0; uint16_t wr_val = 0x0; uint32_t LANE = 0; core_var_ram_base = 0x50; //merlin28_mptwo_functions.h:#define CORE_VAR_RAM_BASE (0x050) //-------------- Merlin16_Programmers_Guide.docx (RAM filed) -------------------- //Core //vco_rate[7:0] //core_cfg_from_pcs: //merlin16_shortfin_internal.c merlin16_shortfin_INTERNAL_update_uc_core_config_word() /* [15:8] : reserved2 [7]: reserved1 [6]:an_los_workaround [5:1]:vco_rate [0]:core_cfg_from_pcs */ //optional core_cfg_from_pcs = 0x0; core_cfg_vco_rate = vco_rate << 1; core_config_word = an_los_workaround + core_cfg_vco_rate + core_cfg_from_pcs; print_log("%s(): program core_config_word 0x%x to ram address 0x%x ...\n", __func__, core_config_word, core_var_ram_base); addr = core_var_ram_base; wr_val = (((core_config_word & 0xFF) << 8) | (core_config_word >> 8)); /* Swapping upper byte and lower byte to compensate for endianness in Serdes 8051 */ /* Micro RAM Word Write */ merlin28_mptwo_wrw_uc_ram(CoreNum, addr,wr_val); } static void merline28_datapath_get_core_out_reset (uint32 CoreNum) { int LANE = 0; print_log("%s(): Datapath Reset (core) in progress \n", __func__); merlin28_reg_prog(datapath_get_core_out_reset, CoreNum, 0x0); } #if 0 static void merline28_datapath_put_core_int_reset (uint32 CoreNum) { int LANE = 0; print_log("%s(): Datapath Reset (core) in progress \n", __func__); merlin28_reg_prog(datapath_put_core_in_reset, CoreNum, 0x0); } #endif static void merlin28_cfg_lane_ram_var (uint32 CoreNum, uint32 LaneNum, uint16_t an_enabled) { int LANE = LaneNum; uint16_t lane_var_ram_base = 0x0; uint16_t lane_config_word = 0x0; uint16_t cl72_auto_polarity_en = 0x0; uint16_t lane_cfg_from_pcs = 0x0; uint16_t lane_an_enabled = 0x0; uint16_t lane_dfe_on = 0x0; uint16_t addr = 0x0; uint16_t wr_val = 0x0; lane_var_ram_base = 0x400 + (0x100*LaneNum); //merlin28_mptwo_functions.h:#define LANE_VAR_RAM_BASE (0x400); #define LANE_VAR_RAM_SIZE (0x100) //-------------- Merlin Programmers Guide.pdf (RAM field) -------------------- //Lane RX //lane_cfg_from_pcs: // 1: get certain lane configuration from AN/PCS HW status (typicall when AN is enabled) // 0: lane configuration is independent of PCS HW status and derived only from user programming //an_enabled: // 1: CL73 or CL37 AN is enabled. // 0: AN is not enabled (HW CL72 would operate in forced mode, This would cause PMD micro to restart the linnk automatically upon PMD link failure) //dfe_on: // 1: DFE enabled; // 0 DFE is not used //force_brdfe_on: Please use 0 //media type[1:0]: Recommended to provide this information for all modes at OSx1 and OSx2, otherwise can be left at default 0 // 00-pcb trace; 01-copper; 10-optics //unreliable_los: This field is used only when media_type is 'optical'; // 1: assume that LOS cannot be trusted; // 0: Assume that LOS is reliable //scramblings_dis: Recommended to provide this information for all modes at OSx1 and OSx2, otherwise can be left at default 0. // 1: RX input may have sustained repeating data patterns, like unscrambled 8B/10B. Currently not supported for DFE on case, please request if required. // 0: RX input data is scrambled //cl72_emulation_en : Feaure not currently supported, use 0 //cl72_auto_polarity_en: // 1: During CL72 if framelock is not achieved within 1ms it will toggle the "rx_pmd_dp_invert" register. It will continue to toggle until lock; // 0: no auto polarity selection (default) //cl72_restart_timeout_en: // 1: This will enable a pmd_rx_restart after 600ms if the link has failed to complete training. // 0: no restart (default) /* [15:10] reserved [9]: cl72_restart_timeout_en [8]: cl72_auto_polarity_en [7]: scrambling_dis [6]: unreliable_los [5:4]: media_type [3]: force_brdfe_on [2]: dfe_on [1]: an_enabled [0]: lane_cfg_from_pcs 1G: default (all 0) XFI+: default (all 0) ->8G-12.5GKR without AN: dfe_on=1* (lane register cl72_ieee_training_enable = 1) SFI copper: dfe_on =1; media_type = "copper_cable" ->AN(1G/10G) or (1G/2.5G): an_enabled = 1*; lane_cfg_from_pcs = 1* nPPI or SFI optical: dfe_on = 0; media_type = "optical", unrealiable_los based on application */ //the firmware test is customized for testing 10GBASE-KR with CL72 enabled. //cl72_auto_polarity_en = uint16_t(0x1<<8); lane_an_enabled = an_enabled << 1; if (an_enabled) lane_cfg_from_pcs = 0x1; else lane_dfe_on = 0x1 << 2; lane_config_word = cl72_auto_polarity_en + lane_dfe_on + lane_an_enabled + lane_cfg_from_pcs; print_log("%s(): program lane_config_word 0x%x to ram address 0x%x ...\n", __func__, lane_config_word, lane_var_ram_base); addr = lane_var_ram_base; wr_val = (((lane_config_word & 0xFF) << 8) | (lane_config_word >> 8)); /* Swapping upper byte and lower byte to compensate for endianness in Serdes 8051 */ /* Micro RAM Word Write */ merlin28_mptwo_wrw_uc_ram(CoreNum, addr,wr_val); } static void merlin28_lane_config_speed(phy_dev_t *phy_dev, uint32 LNK_SPD, int an_enabled) { int CoreNum = phy_dev->core_index; int LANE = phy_dev->lane_index, LaneNum = LANE; phy_serdes_t *phy_serdes = phy_dev->priv; phy_serdes_t *serdes_core = phy_serdes->priv; /* Patch for Second lane 2.5G under 10G VCO */ if (phy_dev->lane_index == 1 && serdes_core->vco == VCO_10G && phy_serdes->current_speed == PHY_SPEED_2500) { merlin28_pmi_write16(phy_dev->core_index, phy_dev->lane_index, 0x3, 0x9270, 0x0021, 0xc000); } //--- Step 10. Lane Configuration print_log("%s(): Step 10. Lane Configuration \n", __func__); if (parse_sim_opts("-d MERLIN_LOAD_FIRMWARE")) { //--- Step 10.a. Configure lane registers print_log("%s(): Step 13.a. Configure lane registers \n", __func__); //Lane General // 1. cl72_ieee_traning_enable if ( (LNK_SPD == MLN_SPD_FORCE_10G_R) || (LNK_SPD == MLN_SPD_FORCE_10G_R_CL74) // 3.6.2.2 c. 8G-12.5GKR without AN || (LNK_SPD == MLN_SPD_AN_10G_KR_IEEE_CL73) || (LNK_SPD == MLN_SPD_AN_10G_KR_IEEE_CL73_CL74) || (LNK_SPD == MLN_SPD_AN_10G_USER_CL73) || (LNK_SPD == MLN_SPD_AN_10G_USER_CL73_CL74)) { if (parse_sim_opts("-d MERLIN_CL72_TRAINING_ENABLE")) { merlin28_pmi_write16(CoreNum, LaneNum, 0x1, 0x0096, 0x0002, 0xfffd); //[1]:cl72_ieee_training_enable 1: enable 10GBASE-KR; 0: disable 10GBASE-KR start-up protocol } } // 2.eee_mode_en: Enable EEE functionality (no officially suppported) // 3.osr_mode_frc, osr_mode_frc_val: override OSR mode input pin from PCS/AN // 4.rx_pmd_dp_invert, tx_pmd_dp_invert: polarity inversion for RX and TX lane //TX Lane* // 1. Analog TX (FIR & HPF): API merlin16_tx_analog_functions.c. It is required to configure the TXFIR according to desired electrical specificaitons //- default values below are expected to be sufficient for bring up and basic BER testing. However, they are only approximate, not optimal and not guaranteed to //- be compliant to all aspects of electrical specs. //- refer to serdes_apply_txfir_cfg(int8_t pre, int8_t main, int8_t post1, int8_t post2) in merlin28_tx_analog_functions.c //serdes_apply_txfir_cfg (0,36,0,0); //10G XFI+ 600m Vpp //serdes_apply_txfir_cfg (0,36,0,0); //10G SFI short traces 600m Vpp //serdes_apply_txfir_cfg (0,27,9,0); //10G SFI long traces (~5dB to connector) 600m Vpp with 6dB TXEQ //serdes_apply_txfir_cfg (0,60,0,0); //AN (1G/10G), AN (1G/2.5G), 2.5G (electrical), 1G (electrica) 1000m Vpp //serdes_apply_txfir_cfg (0,36,0,0); //1G optics (SFI compatible) 600m Vpp //- When TX jitter spec is not stringent, leave en_hpf to default 0. Otherwise set en_hpf = 7. //merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd0a2, 0x0007, 0xfff0); //[3:0]:en_hpf = 0x7; //--- Step 10.b. Configure lane's micro RAM variables print_log("%s(): Step 13.b. Configure lane's micro RAM vaiables \n", __func__); #if 0 if ((LNK_SPD == MLN_SPD_AN_1G_KX_IEEE_CL73) || (LNK_SPD == MLN_SPD_AN_1G_USER_CL73) || (LNK_SPD == MLN_SPD_AN_1G_IEEE_CL37) || (LNK_SPD == MLN_SPD_AN_1G_USER_CL37) || (LNK_SPD == MLN_SPD_AN_10G_KR_IEEE_CL73) || (LNK_SPD == MLN_SPD_AN_10G_KR_IEEE_CL73_CL74) || (LNK_SPD == MLN_SPD_AN_10G_USER_CL73) || (LNK_SPD == MLN_SPD_AN_10G_USER_CL73_CL74) || (LNK_SPD == MLN_SPD_AN_5G_KR_IEEE_CL73) || (LNK_SPD == MLN_SPD_AN_5G_USER_CL73) || (LNK_SPD == MLN_SPD_AN_1G_SGMII) || (LNK_SPD == MLN_SPD_AN_100M_SGMII) || (LNK_SPD == MLN_SPD_AN_10M_SGMII) || (LNK_SPD == MLN_SPD_AN_SGMII_SLAVE) || (LNK_SPD == MLN_SPD_AN_IEEE_CL73) || (LNK_SPD == MLN_SPD_AN_USER_CL73) ) { an_enabled = 0x1; //3.6.2.2 e. AN (1G/10G) or (1G/2.5G): an_enabled = 1 } #endif print_log("%s(): RAM variable an_enabled is %d \n", __func__, an_enabled); merlin28_cfg_lane_ram_var (CoreNum, LaneNum, an_enabled); } //Program lane's speed mode print_log("%s(#%x): \n", __func__,LNK_SPD); //CL36 if (LNK_SPD == MLN_SPD_FORCE_2P5G) { //Force 2P5G print_log("CONFIGURING FOR FORCE 2P5G\n"); if (parse_sim_opts("-d ML_C_VCO_10P3125")) { print_log("... Writing 0x%04x with data mask of 0x%04x to core #%d lane #%d, dev address 0x%02x, register address 0x%04x\n", 0x21, 0xc000, CoreNum, LaneNum, 0x3, 0x9270); merlin28_pmi_write16(CoreNum, LaneNum, 0x3, 0x9270, 0x0021, 0xc000); } merlin28_reg_prog(force_speed_2p5g, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_FORCE_1G) { //Force 1G print_log("CONFIGURING FOR FORCE 1G\n"); merlin28_reg_prog(force_speed_1g, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_1G_KX_IEEE_CL73) { //AN 1G KX - IEEE CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 1G KX - IEEE CL73\n"); merlin28_reg_prog(auto_neg_1g_kx_ieee_cl73, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_1G_USER_CL73) { //AN 1G - USER CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 1G - USER SPACE CL73\n"); merlin28_reg_prog(auto_neg_1g_user_cl73, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_1G_IEEE_CL37) { //AN 1G - IEEE CL37 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 1G - IEEE CL37\n"); merlin28_reg_prog(auto_neg_1g_ieee_cl37, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_1G_USER_CL37) { //AN 1G - USER CL37 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 1G - USER SPACE CL37\n"); merlin28_reg_prog(auto_neg_1g_user_cl37, CoreNum, LaneNum); //CL49 } else if (LNK_SPD == MLN_SPD_FORCE_10G_R) { //Force CL49 10G BASE-R print_log("CONFIGURING FOR FORCE 10G BASE-R \n"); merlin28_reg_prog(force_speed_10g_R, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_FORCE_10G_R_CL74) { //Force CL49 10G BASE-R + CL74 FEC print_log("CONFIGURING FOR FORCE 10G BASE-R with CL74 FEC\n"); merlin28_reg_prog(force_speed_10g_R_cl74_fec, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_10G_KR_IEEE_CL73) { //AN CL49 10G BASE-KR with IEEE CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 10G KR - IEEE CL73\n"); merlin28_reg_prog(auto_neg_10g_kr_cl73, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_10G_KR_IEEE_CL73_CL74) { //AN CL49 10G BASE-KR +CL74 FEC with IEEE CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 10G KR + CL74 FEC - IEEE CL73\n"); merlin28_reg_prog(auto_neg_10g_kr_cl73_cl74_fec, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_10G_USER_CL73) { //AN CL49 10G with User Space CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 10G - User Space CL73 \n"); merlin28_reg_prog(auto_neg_10g_user_cl73, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_10G_USER_CL73_CL74) { //AN CL49 10G with User Space CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 10G + CL74 FEC - User Space CL73 \n"); merlin28_reg_prog(auto_neg_10g_user_cl73_cl74_fec, CoreNum, LaneNum); //CL129 } else if (LNK_SPD == MLN_SPD_FORCE_5G_R) { //Force CL129 5GBASE-R print_log("CONFIGURING FOR FORCE 5G BASE-R\n"); merlin28_reg_prog(force_speed_5g_R, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_FORCE_2P5G_R) { //Force 2P5G BASE-R print_log("CONFIGURING FOR FORCE 2P5G BASE-R\n"); merlin28_reg_prog(force_speed_2p5g_R, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_5G_KR_IEEE_CL73) { //AN CL129 5G BASE-KR with IEEE CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 5G KR - IEEE CL73\n"); merlin28_reg_prog(auto_neg_5g_kr_cl73, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_5G_USER_CL73) { //AN CL129 5G with User Space CL73 //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR AN 5G - User Space CL73 \n"); merlin28_reg_prog(auto_neg_5g_user_cl73, CoreNum, LaneNum); //SGMII } else if (LNK_SPD == MLN_SPD_FORCE_100M) { //Force 100M SGMII print_log("CONFIGURING FOR FORCE 100M SGMII\n"); merlin28_reg_prog(force_speed_100m, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_FORCE_10M) { //Force 10M SGMII print_log("CONFIGURING FOR FORCE 10M SGMII\n"); merlin28_reg_prog(force_speed_10m, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_1G_SGMII) { //AN 1G SGMII //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- if (parse_sim_opts("-d ML_C_VCO_10P3125")) { print_log("Apply 1G credit for 10.3125GHz of VCO "); merlin28_reg_prog(sgmii_an_1g_credit_vco10p3125g, CoreNum, LaneNum); } print_log("CONFIGURING FOR AN 1G SGMII\n"); merlin28_reg_prog(sgmii_an_speed_1g_master, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_100M_SGMII) { //AN 100M SGMII //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- if (parse_sim_opts("-d ML_C_VCO_10P3125")) { print_log("Apply 1G credit for 10.3125GHz of VCO "); merlin28_reg_prog(sgmii_an_1g_credit_vco10p3125g, CoreNum, LaneNum); } print_log("CONFIGURING FOR AN 100M SGMII\n"); merlin28_reg_prog(sgmii_an_speed_100m_master, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_10M_SGMII) { //AN 10M SGMII //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- if (parse_sim_opts("-d ML_C_VCO_10P3125")) { print_log("Apply 1G credit for 10.3125GHz of VCO "); merlin28_reg_prog(sgmii_an_1g_credit_vco10p3125g, CoreNum, LaneNum); } print_log("CONFIGURING FOR AN 100M SGMII\n"); merlin28_reg_prog(sgmii_an_speed_10m_master, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_SGMII_SLAVE) { //AN SGMII SLAVE //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- if (parse_sim_opts("-d ML_C_VCO_10P3125")) { print_log("Apply 1G credit for 10.3125GHz of VCO "); merlin28_reg_prog(sgmii_an_1g_credit_vco10p3125g, CoreNum, LaneNum); } print_log("CONFIGURING FOR SGMII AN SLAVE\n"); merlin28_reg_prog(sgmii_an_slave, CoreNum, LaneNum); //CL73 } else if (LNK_SPD == MLN_SPD_AN_IEEE_CL73) { //CL73 IEEE AN ALL speed //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR CL73 IEEE AN ALL SPEED\n"); merlin28_reg_prog(auto_neg_ieee_cl73, CoreNum, LaneNum); } else if (LNK_SPD == MLN_SPD_AN_USER_CL73) { //CL73 USER AN ALL speed //print_log("AN speed up\n"); //merlin28_reg_prog(AN_speedup, CoreNum, LaneNum); //---- for simulation speed up ----- print_log("CONFIGURING FOR CL73 USER AN ALL SPEED\n"); merlin28_reg_prog(auto_neg_user_cl73, CoreNum, LaneNum); } else { fatal_log("ERROR !!! %s():Merlin Core #%d lane #%d Mode %x is not supported \n", __func__, CoreNum, LaneNum, LNK_SPD); } //relese per-lane's ln_dp_s_rstb. Merlin Programmers Guide.pdf subchapter 3.1 #10.c merlin28_reg_prog(datapath_reset_lane, CoreNum, LaneNum); timeout_ns(100); //delay for simulation debug only print_log("INFO %s(): END Merlin core #%d lane #%d Initialization procedure\n", __func__, CoreNum, LaneNum); } static void merlin28_check_pll_lock (uint32 CoreNum, int LANE, bool VCO_9P375G) { uint32 rd_data; uint32 rd_addr; /* uint32 bpcm_rd_data; uint32 bpcm_rd_addr; */ uint32 pll_lock; int watch_dog; int watch_dog_timer; rd_data = 0; //PowerMgrBlk * switch_blk = pChipEnv->pwr_mgr->get_blk(PowerMgr::BLKTYPE_SWITCH); if (CoreNum < NUMBER_MERLIN_CORES) { rd_addr = (CoreNum<<8) + MERLIN_STATUS; } else { fatal_log("ERROR !!! %s():Merlin Core #%d\n is not supported", __func__, CoreNum); } if (VCO_9P375G) { watch_dog_timer = 150; //should be locked within 150us when VCO is 9.375GHz } else { if (parse_sim_opts("-d MERLIN_LOAD_FIRMWARE")) { watch_dog_timer = 100; //should be locked within 50us when VCO is 10.3125GHz } else { watch_dog_timer = 50; //should be locked within 50us when VCO is 10.3125GHz } } print_log("%s(): Checking Core #%d PLL Lock Status \n", __func__, CoreNum); watch_dog=0; pll_lock = 0; while (!pll_lock) { rd_data = host_reg_read(rd_addr); pll_lock = rd_data & (0x1 << MERLIN_SERDES_STATUS_PLL_LOCK_OFFSET); //[6]: PLL_LOCK msleep(1); watch_dog = watch_dog + 1; if (watch_dog > watch_dog_timer) { fatal_log("ERROR !!! %s(): Merlin Core #%d Lane %d PLL lock checking has timed-out (rd_data=0x%x, pll_lock=0x%x) \n", __func__, CoreNum, LANE, rd_data, pll_lock); } } print_log("%s(): PLL Locked\n", __func__); } static void merlin28_mptwo_poll_uc_dsc_ready_for_cmd_equals_1 (uint32 CoreNum, uint32 LaneNum) { uint32 pmi_rd_data; uint32 L_CoreNum; int LANE = LaneNum; if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x0); //disable BC bridge L_CoreNum = 0x0; } else { L_CoreNum = CoreNum; } //timeout_ns (1000000); //1ms timeout_ns (500000); //500us observed ~480us in waveform msleep(1000); pmi_rd_data = merlin28_pmi_read16(L_CoreNum, LaneNum, 0x1, 0xd00d); if ((pmi_rd_data & 0x0080) == 0x0080) { //uc_dsc_ready_for_cmd@[7] if ((pmi_rd_data & 0x0040) == 0x0040) { //bit 6 is uc_dsc_error_found //EFUN(merlin28_mptwo_pmd_wr_reg(DSC_A_DSC_UC_CTRL,0x80)); //EFUN(wr_uc_dsc_data(0)); fatal_log("%s(): us_dsc_ready for cmd is set and uc_dsc_error is also set pmi_rd_data 0x%04x!!!\n", __func__, pmi_rd_data); } else { print_log("%s(): micro is ready for command ...\n", __func__); } } else { fatal_log("%s(): uc_dsc_ready_for_cmd is not set pmi_rd_data 0x%04x!!!\n", __func__, pmi_rd_data); } if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x1<<8); //re-enable BC bridge } } static void merlin28_mptwo_ucode_load_verify (uint32 CoreNum) { int LANE = 0; uint32_t ucode_len; uint16_t ucode_len_padded, rddata, ram_data, count = 0; uint8_t rdata_lsb; ucode_len = MERLIN_MPTWO_UCODE_IMAGE_SIZE; ucode_len_padded = ((ucode_len + 7) & 0xFFF8); /* Aligning ucode size to 8-byte boundary */ if (ucode_len_padded > UCODE_MAX_SIZE) { /* uCode size should be less than UCODE_MAX_SIZE */ fatal_log("%s(): ERR_CODE_INVALID_UCODE_LEN !!!\n", __func__); } //EFUN(wrc_micro_byte_mode(0x0)); /* Select Word access mode */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xfdff); //EFUN(wrc_micro_mdio_ram_access_mode(0x1)); /* Select Program RAM access through MDIO Register interface mode */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0080, 0xfe7f); //EFUN(wrc_micro_mdio_ram_read_autoinc_en(1)); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0040, 0xffbf); //EFUN(wrc_micro_ram_address(0x0)); /* Start address of Program RAM from where to read ucode */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd201, 0x0000, 0x0000); do { /* ucode_image read 16bits at a time */ if ( 0 && (count%1000) == 0) { print_log("%s(): DEBUG INFO: reading bytes >%d from program ram...\n", __func__, count); } rdata_lsb = (count < ucode_len) ? merlin28_mptwo_ucode_image[count] : 0x0; /* rdata_lsb read from ucode_image; zero padded to 8byte boundary */ count++; rddata = (count < ucode_len) ? merlin28_mptwo_ucode_image[count] : 0x0; /* rdata_msb read from ucode_image; zero padded to 8byte boundary */ count++; rddata = ((rddata << 8) | rdata_lsb); /* 16bit rddata formed from 8bit msb and lsb values read from ucode_image */ //ESTM(ram_data = rdc_micro_ram_rddata()); ram_data = merlin28_pmi_read16(CoreNum, 0x0, 0x1, 0xd204); if (ram_data != rddata) { /* Compare Program RAM ucode to ucode_image (Read to ram_rddata reg auto-increments the ram_address) */ //EFUN_PRINTF(("Ucode_Load_Verify_FAIL: Addr = 0x%x : Read_data = 0x%x : Expected_data = 0x%x\n",(count-2),ram_data,rddata)); //return (_error(ERR_CODE_UCODE_VERIFY_FAIL)); /* Verify uCode FAIL */ fatal_log("%s():Ucode_Load_Verify_FAIL: Addr = 0x%x : Read_data = 0x%x : Expected_data = 0x%x\n", __func__,(count-2),ram_data,rddata); count = 0; break; } } while (count < ucode_len_padded); /* Loop repeated till entire image loaded (upto the 8byte boundary) */ //EFUN(wrc_micro_mdio_ram_read_autoinc_en(0)); merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0000, 0xffbf); //EFUN(wrc_micro_mdio_ram_access_mode(0x2)); /* Select Data RAM access through MDIO Register interface mode */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd202, 0x0100, 0xfe7f); if (count) print_log("Micro code read back verification succeeded\n"); } #if 0 static void host_reg_rbus_write(uint32 addr, uint32 data) { //host_regs_rbus->write32(addr, data); fatal_log("addr 0x%x, data 0x%x\n", addr, data); } static uint32 host_reg_rbus_read(uint32 addr) { //host_regs_rbus->read32(addr); fatal_log("addr 0x%x\n", addr); return 0; } static void check_xport0_port0_link_status(void) { uint32 d32; uint32 polling; uint32 counter; polling = 1; counter = 0; while (polling) { d32 = pChipEnv->tb_rw->read32(0x837f2004); if (d32 == 0x5) { print_log("XPORT port 0 link up successfully\n"); polling =0; } else { timeout_ns(10000); counter = counter+1; if (counter>30) fatal_log("XPORT port0 link up out of time real value = %x \n", d32); } } polling = 1; counter = 0; while (polling) { d32 = pChipEnv->tb_rw->read32(0x837f6004); if (d32 == 0x1) { print_log("XPORT port 1 link up successfully\n"); polling =0; } else { timeout_ns(10000); counter = counter+1; if (counter>30) fatal_log("XPORT port1 link up out of time real value = %x \n", d32); } } } static int merlin28_get_current_inter_phy_type(phy_dev_t *phy_dev) { phy_serdes_t *phy_serdes = phy_dev->priv; uint32_t m4_speed = phy_serdes->serdes_speed_mode; if (!phy_dev->link || phy_serdes->config_speed == PHY_SPEED_AUTO) return INTER_PHY_TYPE_UNKNOWN; switch (m4_speed) { case MLN_SPD_FORCE_100M: return INTER_PHY_TYPE_SGMII; case MLN_SPD_FORCE_1G: return INTER_PHY_TYPE_1000BASE_X; case MLN_SPD_FORCE_2P5G_R: return INTER_PHY_TYPE_2P5GBASE_R; case MLN_SPD_FORCE_2P5G: return INTER_PHY_TYPE_2500BASE_X; case MLN_SPD_FORCE_5G_R: return INTER_PHY_TYPE_5GBASE_R; case MLN_SPD_FORCE_10G_R: return INTER_PHY_TYPE_10GBASE_R; case MLN_SPD_AN_SGMII_SLAVE: case MLN_SPD_AN_1G_SGMII: case MLN_SPD_AN_100M_SGMII: case MLN_SPD_AN_10M_SGMII: return INTER_PHY_TYPE_SGMII; default: break; } return INTER_PHY_TYPE_UNKNOWN; } #endif void merlin28_chk_lane_link_status(phy_dev_t *phy_dev) { uint32 rd_data; uint32 rd_addr; uint32 CoreNum = phy_dev->core_index; uint32 LaneNum = phy_dev->lane_index; int watch_dog; int timeout; int i; int old_link = phy_dev->link; rd_data = 0; rd_addr = (CoreNum<<8) + MERLIN_STATUS; watch_dog=0; timeout = 7000; //observed 130~280us for forced 10G mode; 50~230us for AN 10G mode for(i=0; i<10; i++) { /* To filter out certain speed false link up */ rd_data = host_reg_read(rd_addr); phy_dev->link = ((rd_data>>(4+LaneNum)) & 0x1) > 0; //[2]: Lane link status if (phy_dev->link == 0) break; msleep(1); } if (!phy_dev->link) goto end; /* Below is for link up only */ for (i=0; i<10; i++) { rd_data = merlin28_pmi_read16(CoreNum, LaneNum, 0x3, 0xc475); if (rd_data & 0x1f) // Wait for speed result break; msleep(1); } if ((rd_data & 0x1f) == 0) /* False link up */ { phy_dev->link = 0; goto end; } switch(rd_data & 0x1f) { case 0xf: phy_dev->speed = PHY_SPEED_10000; break; case 9: phy_dev->speed = PHY_SPEED_5000; break; case 3: phy_dev->speed = PHY_SPEED_2500; break; case 2: phy_dev->speed = PHY_SPEED_1000; break; case 1: phy_dev->speed = PHY_SPEED_100; break; } phy_dev->duplex = PHY_DUPLEX_FULL; /* If it first time link up and the speed is 5G or 10G, we do intensive check to filter out false link up */ if (!old_link && phy_dev->speed == PHY_SPEED_5000) { int error_cnt = 0; int i; #define TotalCheckCycles 200 #define ErrorCountLinkDown 1 for (i=0; i < TotalCheckCycles; i++) { rd_data = merlin28_pmi_read16(CoreNum, LaneNum, 3, 0xc466); // Checking BAD_R_TYPE, R_TYPE_E, Latched_RX_E or current RX_E if ((rd_data & (1<<7)) || (rd_data&0x7) == 4 || ((rd_data>>12) & 0xf) == 0xf || ((rd_data>>12) & 0xf) == 0 ) { error_cnt++; if (error_cnt >= ErrorCountLinkDown) { phy_dev->link = 0; printk("Serdes %d False Link Up with Error Symbol 0x%04x at 3.c466h %d times at speed %dMbps\n", phy_dev->addr, rd_data, error_cnt, phy_dev->speed); goto end; } } msleep(1); } } /* If this is the first time link up and speed is 1G, check the mis-link up between 1000Base-X vs. SGMII */ if (!old_link && (phy_dev->speed == PHY_SPEED_1000)) { int error_cnt = 0; int i; int rd; rd_data = merlin28_pmi_read16(CoreNum, LaneNum, 3, 0xc460); /* Check Error counter */ for (i=0; i < TotalCheckCycles; i++) { rd = merlin28_pmi_read16(CoreNum, LaneNum, 3, 0xc460); if (rd_data != rd) { error_cnt++; if (error_cnt >= ErrorCountLinkDown) { phy_dev->link = 0; printk("Serdes %d 1G False Link Up with error at 3.c460h 0x%04x from 0x%04x %d times\n", phy_dev->addr, rd, rd_data, error_cnt); goto end; } } msleep(1); } } end: return; } #if 0 static void merlin28_pcs_loopback(int CoreNum) { print_log("%s(): PCS Loopback Core #%d \n", __func__, CoreNum); merlin28_pmi_write16(CoreNum, 0x0, 0x3, 0x9109, 0x1001, 0xefff); } #endif static int phy_speed_to_merlin28_speed(phy_dev_t *phy_dev) { phy_serdes_t *phy_serdes = phy_dev->priv; switch(phy_dev->current_inter_phy_type) { case INTER_PHY_TYPE_SGMII: if (phy_serdes->sfp_module_type != SFP_FIXED_PHY) /* SFP module */ { phy_serdes->serdes_speed_mode = MLN_SPD_AN_SGMII_SLAVE; phy_dev->an_enabled = 1; } else /* Copper PHY, no AN support */ { phy_dev->an_enabled = 0; if (phy_serdes->config_speed == PHY_SPEED_100) phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_100M; else phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_1G; } break; case INTER_PHY_TYPE_10GBASE_R: phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_10G_R; phy_dev->an_enabled = 0; //phy_serdes->serdes_speed_mode = MLN_SPD_AN_10G_KR_IEEE_CL73_CL74; //phy_dev->an_enabled = 1; break; case INTER_PHY_TYPE_5GBASE_R: phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_5G_R; phy_dev->an_enabled = 0; //phy_serdes->serdes_speed_mode = MLN_SPD_AN_5G_KR_IEEE_CL73; //phy_dev->an_enabled = 1; break; case INTER_PHY_TYPE_2500BASE_X: phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_2P5G; phy_dev->an_enabled = 0; break; case INTER_PHY_TYPE_2P5GBASE_R: phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_2P5G_R; phy_dev->an_enabled = 0; break; case INTER_PHY_TYPE_1000BASE_X: phy_serdes->serdes_speed_mode = MLN_SPD_FORCE_1G; phy_dev->an_enabled = 0; break; default: return -1; } return 0; } static int merlin28_set_status_for_speed_change(phy_dev_t *phy_dev) { merlin28_reg_prog(change_speed, phy_dev->core_index, phy_dev->lane_index); return 0; } static phy_dev_t *phy_dev_lanes[MAX_LANES_PER_CORE]; static phy_dev_t *phy_lane0, *phy_lane1; static int max_lanes; static int merlin28_speed_set_core(phy_dev_t *phy_dev); int merlin28_speed_set(phy_dev_t *phy_dev, phy_speed_t speed, phy_duplex_t duplex) { phy_serdes_t *phy_serdes = phy_dev->priv; if (phy_speed_to_merlin28_speed(phy_dev) == -1) return 0; if (phy_serdes->cur_power_level == SERDES_POWER_DOWN) return 0; merlin28_speed_set_core(phy_dev); return 0; } static int merlin28_get_vco(phy_dev_t *phy_dev) { int vco; if ((phy_dev->lane_index == 0 || (phy_lane0 && phy_lane0->link)) && (phy_lane0->current_inter_phy_type == INTER_PHY_TYPE_5GBASE_R || phy_lane0->current_inter_phy_type == INTER_PHY_TYPE_2P5GBASE_R)) vco = VCO_10G; else vco = VCO_9P375G; return vco; } static int merlin28_speed_set_core(phy_dev_t *phy_dev) { int vco, org_vco; int vco_rate_real; uint16_t vco_rate; phy_serdes_t *phy_serdes = phy_dev->priv; uint32 CoreNum = phy_dev->core_index; uint32 LANE = phy_dev->lane_index; phy_serdes_t *serdes_core = phy_serdes->priv; int lane; print_log("%s(): Step 7 Config Speed to %d\n", __func__, phy_serdes->serdes_speed_mode); vco = merlin28_get_vco(phy_dev); merlin28_set_status_for_speed_change(phy_dev); restore_regs_lane(CoreNum, phy_dev->lane_index); org_vco = serdes_core->vco; if (vco != serdes_core->vco) { if (max_lanes > 1) { for(lane = 0; lane < max_lanes; lane++) { if (phy_dev == phy_dev_lanes[lane]) continue; merlin28_set_status_for_speed_change(phy_dev_lanes[lane]); restore_regs_lane(CoreNum, phy_dev_lanes[lane]->lane_index); } } restore_regs_core(CoreNum, 0); // Restore all registers to default values for new speed programing save_reg_mode = SAVE_REG_CORE; //---Step 5. PLL Configuration print_log("%s(): Step 8. PLL Configuration \n", __func__); if (vco == VCO_9P375G) { print_log("%s(): Initialize 9.375G VCO programming \n", __func__); merlin28_reg_prog(Initialize_9p375_VCO, CoreNum, phy_dev->lane_index); } //--- PLL/PMD setup configuration if (parse_sim_opts("-d ML_REFCLK_156P25")) { if (vco == VCO_9P375G) { print_log("%s(): PMD Setup 156.25MHz, 12.5GHz VCO programming \n", __func__); merlin28_reg_prog(PMD_setup_156p25_9p375_VCO, CoreNum, phy_dev->lane_index); } else { print_log("%s(): PMD Setup 156.25MHz, 10.3125GHz VCO programming \n", __func__); merlin28_reg_prog(PMD_setup_156p25_10p3125_VCO, CoreNum, phy_dev->lane_index); //CoreNum = 0x1f for broadcast } } else { //default to 50MHz if (vco == VCO_9P375G) { print_log("%s(): PMD Setup 50MHz, 9.375GHz VCO programming \n", __func__); merlin28_reg_prog(PMD_setup_50_9p375_VCO, CoreNum, 0x0); } else { print_log("%s(): PMD Setup 50MHz, 10.3125GHz VCO programming \n", __func__); merlin28_reg_prog(PMD_setup_50_10p3125_VCO, CoreNum, 0x0); } } if (parse_sim_opts("-d MERLIN_LOAD_FIRMWARE")) { //---Step 6. Configure Core level regsiter print_log("%s(): Step 9. Configure Core level regsiter \n", __func__); //1. hearbeat_count_1us : set before firmware download (already done in step 3) //2. PLL: (already done in step 5.) //3. RefClk related //4. rx/tx_lane_addr //--- Step ??. Set core_congif_from_pcs print_log("%s(): Step 10. Set core_congif_from_pcs \n", __func__); //?? if (vco == VCO_9P375G) { vco_rate_real = (9.375 * 4.0) - 22.0; //28 } else { vco_rate_real = (10.3125 * 4.0) - 22.0; //19.25 } vco_rate = vco_rate_real; print_log("%s(): RAM variable vco_rate is %d \n", __func__, vco_rate); merlin28_cfg_core_ram_var (CoreNum, vco_rate); } save_reg_mode = SAVE_REG_NO; //--- Step 9. Reset Datapath (Core DP soft reset) print_log("%s(): Step 9. Reset Datapath (core) \n", __func__); merline28_datapath_get_core_out_reset (CoreNum); //--- --- check PLL lock status merlin28_check_pll_lock (CoreNum, phy_dev->lane_index, vco); // pass lane just for logging serdes_core->vco = vco; } //---------------------------------- //--- Step 7. apply ln_dp_s_rstb (Lane DP soft reset) to all lanes //print_log("%s(): Step 7. Apply ln_dp_s_rstb to all lanes \n", __func__); //merlin28_reg_prog(en_datapath_reset_lane, CoreNum, phy_dev->lane_index); // included in change_speed() //---Step 8. Ensure the blocks drving pvt_mon and res_cal input pins are enabled. //print_log("%s(): Step 8. Ensure the blocks drving pvt_mon and res_cal input pins are enabled \n", __func__); if (CoreNum == PMI_BC_ADDRESS) { host_reg_write(MERLIN_PMI_BC_CNTRL,0x0); //disable BC bridge } save_reg_mode = SAVE_REG_LANE; merlin28_lane_config_speed(phy_dev, phy_serdes->serdes_speed_mode, phy_dev->an_enabled); if (vco != org_vco && max_lanes > 1) { //Program the other lane's speed mode for(lane=0; lanepriv; if (phy_dev == phy_dev_lanes[lane]) continue; merlin28_lane_config_speed(phy_dev_lanes[lane], _phy_serdes->serdes_speed_mode, phy_dev_lanes[lane]->an_enabled); } } save_reg_mode = SAVE_REG_NO; print_log("INFO %s(): END Merlin Initialization procedure\n", __func__); return 0; } static void merlin28_mptwo_uc_active_enable (uint32 CoreNum, uint8_t enable) { //EFUN(wrc_uc_active(enable)); uint16_t wr_val; wr_val = (enable & 0x1) << 0x6; merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xd0f2, wr_val, 0xffbf); //uc_active } int merlin28_serdes_init(phy_dev_t *phy_dev) { phy_serdes_t *phy_serdes = phy_dev->priv; phy_serdes_t *serdes_core = phy_serdes->priv; uint32 CoreNum = phy_dev->core_index; uint32 LANE = phy_dev->lane_index; int lane; phy_dev_lanes[max_lanes] = phy_dev; max_lanes++; switch(phy_dev->lane_index) { case 0: phy_lane0 = phy_dev; break; case 1: phy_lane1 = phy_dev; break; } if (serdes_core->inited) { merlin28_lane_init(phy_dev); return 0; } //--- Step 0 powerup/reset sequence if (!serdes_core->inited) { print_log("--- Step 0 powerup/reset sequence of core #%d at address %d\n", CoreNum, phy_dev->addr); _merlin28_core_power_op(phy_dev, SERDES_POWER_DOWN); _merlin28_core_power_op(phy_dev, SERDES_POWER_UP); for (lane = 0; lane < MAX_LANES_PER_CORE; lane++) merlin28_powerdn_lane (phy_dev->core_index, lane); } merlin28_powerup_lane (phy_dev->core_index, phy_dev->lane_index); if (serdes_core->inited) return 0; merlin28_core_init(phy_dev); timeout_ns(1000); /* Set Broadcast MDIO address to the same address to avoid occupying 0 address */ merlin28_pmi_write16(CoreNum, 0x0, 0x1, 0xffdc, phy_dev->addr, 0xffe0); if (parse_sim_opts("-d MERLIN_LOAD_FIRMWARE") ) { merlin28_pmi_write16(CoreNum, phy_dev->lane_index, 0x1, 0xd0d4, 0x0002, 0xfffc); //[1:0] prbs_chk_en_timer_mode 2'b10: use heatbeat_toggle_1us for the timer //---Step 4.a Step 4.a Set uc_active = 1 print_log("%s(): Step 4.a Set uc_active = 1 \n", __func__); merlin28_mptwo_uc_active_enable(CoreNum, 0x1); //--- --- Load firmware ? TBD //--- Step 4. Assert micro reset (merlin28_shortfin_uc_reset(1)) print_log("%s(): Step 4. Assert micro reset \n", __func__); merlin28_uc_reset(CoreNum, 0x1); //-----------Micro code load and verify print_log("%s(): Step 4.a.2 Micro code load and verify \n", __func__); merlin28_load_firmware(phy_dev); if (parse_sim_opts("-d MERLIN_UC_VERIFY_CRC")) { //verify CRC merlin28_mptwo_ucode_load_verify (CoreNum); } //---Step 4.b De-assert 8051 reset print_log("%s(): Step 4.b De-assert micro reset \n", __func__); merlin28_uc_reset(CoreNum, 0x0); //---Step 4.c wait for uc_dsc_ready_for_cmd = 1 print_log("%s(): Step 4.c wait for uc_dsc_ready_for_cmd = 1 \n", __func__); merlin28_mptwo_poll_uc_dsc_ready_for_cmd_equals_1 (CoreNum, phy_dev->lane_index); } #if 0 if (phy_serdes->signal_detect_gpio != -1) wr_ext_los_en(1); #endif serdes_core->inited = 1; phy_serdes->inited = 1; print_log("INFO %s(): END Merlin Initialization procedure\n", __func__); return 0; }