void cvmx_qlm_display_registers(int qlm) { int num_lanes = cvmx_qlm_get_lanes(qlm); int lane; const __cvmx_qlm_jtag_field_t *ptr = cvmx_qlm_jtag_get_field(); cvmx_dprintf("%29s", "Field[<stop bit>:<start bit>]"); for (lane = 0; lane < num_lanes; lane++) cvmx_dprintf("\t Lane %d", lane); cvmx_dprintf("\n"); while (ptr != NULL && ptr->name) { cvmx_dprintf("%20s[%3d:%3d]", ptr->name, ptr->stop_bit, ptr->start_bit); for (lane = 0; lane < num_lanes; lane++) { uint64_t val; int tx_byp = 0; /* Make sure serdes_tx_byp is set for displaying TX amplitude and TX demphasis field values. */ if (strncmp(ptr->name, "biasdrv_", 8) == 0 || strncmp(ptr->name, "tcoeff_", 7) == 0) { tx_byp = cvmx_qlm_jtag_get(qlm, lane, "serdes_tx_byp"); if (tx_byp == 0) { cvmx_dprintf("\t \t"); continue; } } val = cvmx_qlm_jtag_get(qlm, lane, ptr->name); cvmx_dprintf("\t%4llu (0x%04llx)", (unsigned long long)val, (unsigned long long)val); } cvmx_dprintf("\n"); ptr++; } }
/** * 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); }
int do_qlm(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int num_qlm; int qlm; int c_arg; /* Make sure we got the correct number of arguments */ if (((argc - 2) % 3) != 0) { printf("Invalid number of arguments %d\n", argc); usage(argv); return -1; } num_qlm = cvmx_qlm_get_num(); qlm = simple_strtoul(argv[1], NULL, 0); if ((qlm < 0) || (qlm >= num_qlm)) { printf("Invalid qlm number\n"); return -1; } if (argc >= 5) { int lane; uint64_t value; int field_count; int num_fields = (argc - 2) / 3; c_arg = 2; for (field_count = 0; field_count < num_fields; field_count++) { char name[30]; if (!strcmp(argv[c_arg], "all")) lane = -1; else { lane = (int)simple_strtol(argv[c_arg], NULL, 0); if (lane >= cvmx_qlm_get_lanes(qlm)) { printf("Invalid lane passed\n"); return -1; } } strcpy(name, argv[c_arg + 1]); value = convert_number(argv[c_arg + 2]); cvmx_qlm_jtag_set(qlm, lane, name, value); /* Assert serdes_tx_byp to force the new settings to override the QLM default. */ if (strncmp(name, "biasdrv_", 8) == 0 || strncmp(name, "tcoeff_", 7) == 0) cvmx_qlm_jtag_set(qlm, lane, "serdes_tx_byp", 1); c_arg += 3; if (lane == -1) break; } } else cvmx_qlm_display_registers(qlm); return 0; }
/** * Get a field in a QLM JTAG chain * * @param qlm QLM to get * @param lane Lane in QLM to get * @param name String name of field * * @return JTAG field value */ uint64_t cvmx_qlm_jtag_get(int qlm, int lane, const char *name) { const __cvmx_qlm_jtag_field_t *field = __cvmx_qlm_lookup_field(name); int qlm_jtag_length = cvmx_qlm_jtag_get_length(); int num_lanes = cvmx_qlm_get_lanes(qlm); if (!field) return 0; /* Capture the current settings */ cvmx_helper_qlm_jtag_capture(qlm); /* Shift past lanes we don't care about. CN6XXX shifts lane 3 first */ cvmx_helper_qlm_jtag_shift_zeros(qlm, qlm_jtag_length * (num_lanes-1-lane)); /* Shift to the start of the field */ cvmx_helper_qlm_jtag_shift_zeros(qlm, field->start_bit); /* Shift out the value and return it */ return cvmx_helper_qlm_jtag_shift(qlm, field->stop_bit - field->start_bit + 1, 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(); }