/** * Due to errata G-720, the 2nd order CDR circuit on CN52XX pass * 1 doesn't work properly. The following code disables 2nd order * CDR for the specified QLM. * * @param qlm QLM to disable 2nd order CDR for. */ void __cvmx_helper_errata_qlm_disable_2nd_order_cdr(int qlm) { int lane; /* Apply the workaround only once. */ cvmx_ciu_qlm_jtgd_t qlm_jtgd; qlm_jtgd.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGD); if (qlm_jtgd.s.select != 0) return; cvmx_helper_qlm_jtag_init(); /* We need to load all four lanes of the QLM, a total of 1072 bits */ for (lane = 0; lane < 4; lane++) { /* * Each lane has 268 bits. We need to set * cfg_cdr_incx<67:64> = 3 and * cfg_cdr_secord<77> = 1. * All other bits are zero. Bits go in LSB first, * so start off with the zeros for bits <63:0>. */ cvmx_helper_qlm_jtag_shift_zeros(qlm, 63 - 0 + 1); /* cfg_cdr_incx<67:64>=3 */ cvmx_helper_qlm_jtag_shift(qlm, 67 - 64 + 1, 3); /* Zeros for bits <76:68> */ cvmx_helper_qlm_jtag_shift_zeros(qlm, 76 - 68 + 1); /* cfg_cdr_secord<77>=1 */ cvmx_helper_qlm_jtag_shift(qlm, 77 - 77 + 1, 1); /* Zeros for bits <267:78> */ cvmx_helper_qlm_jtag_shift_zeros(qlm, 267 - 78 + 1); } cvmx_helper_qlm_jtag_update(qlm); }
/** * Set a field in a QLM JTAG chain * * @param qlm QLM to set * @param lane Lane in QLM to set, or -1 for all lanes * @param name String name of field * @param value Value of the field */ void cvmx_qlm_jtag_set(int qlm, int lane, const char *name, uint64_t value) { int i, l; uint32_t shift_values[CVMX_QLM_JTAG_UINT32]; int num_lanes = cvmx_qlm_get_lanes(qlm); const __cvmx_qlm_jtag_field_t *field = __cvmx_qlm_lookup_field(name); int qlm_jtag_length = cvmx_qlm_jtag_get_length(); int total_length = qlm_jtag_length * num_lanes; int bits = 0; if (!field) return; /* Get the current state */ cvmx_helper_qlm_jtag_capture(qlm); for (i = 0; i < CVMX_QLM_JTAG_UINT32; i++) shift_values[i] = cvmx_helper_qlm_jtag_shift(qlm, 32, 0); /* Put new data in our local array */ for (l = 0; l < num_lanes; l++) { uint64_t new_value = value; int bits; int adj_lanes; if ((l != lane) && (lane != -1)) continue; if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) adj_lanes = l * qlm_jtag_length; else adj_lanes = (num_lanes - 1 - l) * qlm_jtag_length; for (bits = field->start_bit + adj_lanes; bits <= field->stop_bit + adj_lanes; bits++) { if (new_value & 1) shift_values[bits / 32] |= 1 << (bits & 31); else shift_values[bits / 32] &= ~(1 << (bits & 31)); new_value >>= 1; } } /* Shift out data and xor with reference */ while (bits < total_length) { uint32_t shift = shift_values[bits / 32] ^ __cvmx_qlm_jtag_xor_ref[qlm][bits / 32]; int width = total_length - bits; if (width > 32) width = 32; cvmx_helper_qlm_jtag_shift(qlm, width, shift); bits += 32; } /* Update the new data */ cvmx_helper_qlm_jtag_update(qlm); /* Always give the QLM 1ms to settle after every update. This may not always be needed, but some of the options make significant electrical changes */ cvmx_wait_usec(1000); }
void __cvmx_helper_errata_qlm_disable_2nd_order_cdr(int qlm) { int lane; cvmx_helper_qlm_jtag_init(); for (lane = 0; lane < 4; lane++) { cvmx_helper_qlm_jtag_shift_zeros(qlm, 63 - 0 + 1); cvmx_helper_qlm_jtag_shift(qlm, 67 - 64 + 1, 3); cvmx_helper_qlm_jtag_shift_zeros(qlm, 76 - 68 + 1); cvmx_helper_qlm_jtag_shift(qlm, 77 - 77 + 1, 1); cvmx_helper_qlm_jtag_shift_zeros(qlm, 267 - 78 + 1); } cvmx_helper_qlm_jtag_update(qlm); }
/** * Due to errata G-720, the 2nd order CDR circuit on CN52XX pass * 1 doesn't work properly. The following code disables 2nd order * CDR for the specified QLM. * * @qlm: QLM to disable 2nd order CDR for. */ void __cvmx_helper_errata_qlm_disable_2nd_order_cdr(int qlm) { int lane; cvmx_helper_qlm_jtag_init(); /* We need to load all four lanes of the QLM, a total of 1072 bits */ for (lane = 0; lane < 4; lane++) { /* * Each lane has 268 bits. We need to set * cfg_cdr_incx<67:64> = 3 and cfg_cdr_secord<77> = * 1. All other bits are zero. Bits go in LSB first, * so start off with the zeros for bits <63:0>. */ cvmx_helper_qlm_jtag_shift_zeros(qlm, 63 - 0 + 1); /* cfg_cdr_incx<67:64>=3 */ cvmx_helper_qlm_jtag_shift(qlm, 67 - 64 + 1, 3); /* Zeros for bits <76:68> */ cvmx_helper_qlm_jtag_shift_zeros(qlm, 76 - 68 + 1); /* cfg_cdr_secord<77>=1 */ cvmx_helper_qlm_jtag_shift(qlm, 77 - 77 + 1, 1); /* Zeros for bits <267:78> */ cvmx_helper_qlm_jtag_shift_zeros(qlm, 267 - 78 + 1); } cvmx_helper_qlm_jtag_update(qlm); }
int checkboard(void) { int core_mVolts, dram_mVolts; debug("In %s\n", __func__); if (octeon_show_info()) { int mcu_rev_maj = 0; int mcu_rev_min = 0; if (twsii_mcu_read(0x00) == 0xa5 && twsii_mcu_read(0x01) == 0x5a) { gd->arch.mcu_rev_maj = mcu_rev_maj = twsii_mcu_read(2); gd->arch.mcu_rev_min = mcu_rev_min = twsii_mcu_read(3); } core_mVolts = read_ispPAC_mvolts_avg(10, BOARD_ISP_TWSI_ADDR, 8); dram_mVolts = read_ispPAC_mvolts_avg(10, BOARD_ISP_TWSI_ADDR, 7); char mcu_ip_msg[64] = { 0 }; if (twsii_mcu_read(0x14) == 1) sprintf(mcu_ip_msg, "MCU IPaddr: %d.%d.%d.%d, ", twsii_mcu_read(0x10), twsii_mcu_read(0x11), twsii_mcu_read(0x12), twsii_mcu_read(0x13)); printf("MCU rev: %d.%02d, %sCPU voltage: %d.%03d DDR voltage: %d.%03d\n", gd->arch.mcu_rev_maj, gd->arch.mcu_rev_min, mcu_ip_msg, core_mVolts / 1000, core_mVolts % 1000, dram_mVolts / 1000, dram_mVolts % 1000); #define LED_CHARACTERS 8 char tmp[10]; int characters, idx = 0, value = core_mVolts; idx = sprintf(tmp, "%lu ", gd->cpu_clk); characters = LED_CHARACTERS - idx; if (value / 1000) { idx += sprintf(tmp + idx, "%d", value / 1000); characters = LED_CHARACTERS - idx; } characters -= 1; /* Account for decimal point */ value %= 1000; value = DIV_ROUND_UP(value, ipow(10, max(3 - characters, 0))); idx += sprintf(tmp + idx, ".%0*d", min(3, characters), value); /* Display CPU speed and voltage on display */ octeon_led_str_write(tmp); } else { octeon_led_str_write("Boot "); } #if 0 /* DEBUG use only */ { char *eptr = getenv("jtag_ic50"); if (eptr) { /* Do experimental JTAG changes for pass 1.1 */ int qlm_num; int ic50_val = simple_strtol(eptr, NULL, 10); int ir50_val = ic50_val; eptr = getenv("jtag_ir50"); if (eptr) ir50_val = simple_strtol(eptr, NULL, 10); else puts("Warning: jtag_ir50 val not set, using ic50 value\n"); printf("Setting QLM DAC ir50=0x%x, ic50=0x%x via JTAG chain\n", ir50_val, ic50_val); /* New ss are xored with default values, which are 0x11 */ ic50_val ^= 0x11; ir50_val ^= 0x11; cvmx_helper_qlm_jtag_init(); for (qlm_num = 0; qlm_num < 3; qlm_num++) { cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 34); cvmx_helper_qlm_jtag_shift(qlm_num, 5, ir50_val); /* ir50dac */ cvmx_helper_qlm_jtag_shift(qlm_num, 5, ic50_val); /* ic50dac */ cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 300 - 44); cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 34); cvmx_helper_qlm_jtag_shift(qlm_num, 5, ir50_val); /* ir50dac */ cvmx_helper_qlm_jtag_shift(qlm_num, 5, ic50_val); /* ic50dac */ cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 300 - 44); cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 34); cvmx_helper_qlm_jtag_shift(qlm_num, 5, ir50_val); /* ir50dac */ cvmx_helper_qlm_jtag_shift(qlm_num, 5, ic50_val); /* ic50dac */ cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 300 - 44); cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 34); cvmx_helper_qlm_jtag_shift(qlm_num, 5, ir50_val); /* ir50dac */ cvmx_helper_qlm_jtag_shift(qlm_num, 5, ic50_val); /* ic50dac */ cvmx_helper_qlm_jtag_shift_zeros(qlm_num, 300 - 44); cvmx_helper_qlm_jtag_update(qlm_num); } } } #endif return 0; }
/** * Initialize the QLM layer */ void cvmx_qlm_init(void) { int qlm; int qlm_jtag_length; char *qlm_jtag_name = "cvmx_qlm_jtag"; int qlm_jtag_size = CVMX_QLM_JTAG_UINT32 * 8 * sizeof(uint32_t); static uint64_t qlm_base = 0; const cvmx_bootmem_named_block_desc_t *desc; if (OCTEON_IS_OCTEON3()) return; #ifndef CVMX_BUILD_FOR_LINUX_HOST /* Skip actual JTAG accesses on simulator */ if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) return; #endif qlm_jtag_length = cvmx_qlm_jtag_get_length(); if (sizeof(uint32_t) * qlm_jtag_length > (int)sizeof(__cvmx_qlm_jtag_xor_ref[0]) * 8) { cvmx_dprintf("ERROR: cvmx_qlm_init: JTAG chain larger than XOR ref size\n"); return; } /* No need to initialize the initial JTAG state if cvmx_qlm_jtag named block is already created. */ if ((desc = cvmx_bootmem_find_named_block(qlm_jtag_name)) != NULL) { #ifdef CVMX_BUILD_FOR_LINUX_HOST char buffer[qlm_jtag_size]; octeon_remote_read_mem(buffer, desc->base_addr, qlm_jtag_size); memcpy(__cvmx_qlm_jtag_xor_ref, buffer, qlm_jtag_size); #else __cvmx_qlm_jtag_xor_ref = cvmx_phys_to_ptr(desc->base_addr); #endif /* Initialize the internal JTAG */ cvmx_helper_qlm_jtag_init(); return; } /* Create named block to store the initial JTAG state. */ qlm_base = cvmx_bootmem_phy_named_block_alloc(qlm_jtag_size, 0, 0, 128, qlm_jtag_name, CVMX_BOOTMEM_FLAG_END_ALLOC); if (qlm_base == -1ull) { cvmx_dprintf("ERROR: cvmx_qlm_init: Error in creating %s named block\n", qlm_jtag_name); return; } #ifndef CVMX_BUILD_FOR_LINUX_HOST __cvmx_qlm_jtag_xor_ref = cvmx_phys_to_ptr(qlm_base); #endif memset(__cvmx_qlm_jtag_xor_ref, 0, qlm_jtag_size); /* Initialize the internal JTAG */ cvmx_helper_qlm_jtag_init(); /* Read the XOR defaults for the JTAG chain */ for (qlm = 0; qlm < cvmx_qlm_get_num(); qlm++) { int i; int num_lanes = cvmx_qlm_get_lanes(qlm); /* Shift all zeros in the chain to make sure all fields are at reset defaults */ cvmx_helper_qlm_jtag_shift_zeros(qlm, qlm_jtag_length * num_lanes); cvmx_helper_qlm_jtag_update(qlm); /* Capture the reset defaults */ cvmx_helper_qlm_jtag_capture(qlm); /* Save the reset defaults. This will shift out too much data, but the extra zeros don't hurt anything */ for (i = 0; i < CVMX_QLM_JTAG_UINT32; i++) __cvmx_qlm_jtag_xor_ref[qlm][i] = cvmx_helper_qlm_jtag_shift(qlm, 32, 0); } #ifdef CVMX_BUILD_FOR_LINUX_HOST /* Update the initial state for oct-remote utils. */ { char buffer[qlm_jtag_size]; memcpy(buffer, &__cvmx_qlm_jtag_xor_ref, qlm_jtag_size); octeon_remote_write_mem(qlm_base, buffer, qlm_jtag_size); } #endif /* Apply all QLM errata workarounds. */ __cvmx_qlm_speed_tweak(); __cvmx_qlm_pcie_idle_dac_tweak(); }