static int msenc_setup_ucode_image(struct platform_device *dev, u32 *ucode_ptr, const struct firmware *ucode_fw) { struct msenc *m = get_msenc(dev); /* image data is little endian. */ struct msenc_ucode_v1 ucode; int w; /* copy the whole thing taking into account endianness */ for (w = 0; w < ucode_fw->size / sizeof(u32); w++) ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]); ucode.bin_header = (struct msenc_ucode_bin_header_v1 *)ucode_ptr; /* endian problems would show up right here */ if (ucode.bin_header->bin_magic != 0x10de) { dev_err(&dev->dev, "failed to get firmware magic"); return -EINVAL; } if (ucode.bin_header->bin_ver != 1) { dev_err(&dev->dev, "unsupported firmware version"); return -ENOENT; } /* shouldn't be bigger than what firmware thinks */ if (ucode.bin_header->bin_size > ucode_fw->size) { dev_err(&dev->dev, "ucode image size inconsistency"); return -EINVAL; } nvhost_dbg_info("ucode bin header: magic:0x%x ver:%d size:%d", ucode.bin_header->bin_magic, ucode.bin_header->bin_ver, ucode.bin_header->bin_size); nvhost_dbg_info("ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d", ucode.bin_header->os_bin_header_offset, ucode.bin_header->os_bin_data_offset, ucode.bin_header->os_bin_size); ucode.os_header = (struct msenc_ucode_os_header_v1 *) (((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset); nvhost_dbg_info("os ucode header: os code (offset,size): 0x%x, 0x%x", ucode.os_header->os_code_offset, ucode.os_header->os_code_size); nvhost_dbg_info("os ucode header: os data (offset,size): 0x%x, 0x%x", ucode.os_header->os_data_offset, ucode.os_header->os_data_size); nvhost_dbg_info("os ucode header: num apps: %d", ucode.os_header->num_apps); m->os.size = ucode.bin_header->os_bin_size; m->os.bin_data_offset = ucode.bin_header->os_bin_data_offset; m->os.code_offset = ucode.os_header->os_code_offset; m->os.data_offset = ucode.os_header->os_data_offset; m->os.data_size = ucode.os_header->os_data_size; return 0; }
/* Calculate and update M/N/PL as well as pll->freq ref_clk_f = clk_in_f / src_div = clk_in_f; (src_div = 1 on gk20a) u_f = ref_clk_f / M; PLL output = vco_f = u_f * N = ref_clk_f * N / M; gpc2clk = target clock frequency = vco_f / PL; gpcclk = gpc2clk / 2; */ static int clk_config_pll(struct clk_gk20a *clk, struct pll *pll, struct pll_parms *pll_params, u32 *target_freq, bool best_fit) { u32 min_vco_f, max_vco_f; u32 best_M, best_N; u32 low_PL, high_PL, best_PL; u32 m, n, n2; u32 target_vco_f, vco_f; u32 ref_clk_f, target_clk_f, u_f; u32 delta, lwv, best_delta = ~0; int pl; BUG_ON(target_freq == NULL); nvhost_dbg_fn("request target freq %d MHz", *target_freq); ref_clk_f = pll->clk_in; target_clk_f = *target_freq; max_vco_f = pll_params->max_vco; min_vco_f = pll_params->min_vco; best_M = pll_params->max_M; best_N = pll_params->min_N; best_PL = pll_params->min_PL; target_vco_f = target_clk_f + target_clk_f / 50; if (max_vco_f < target_vco_f) max_vco_f = target_vco_f; high_PL = (max_vco_f + target_vco_f - 1) / target_vco_f; high_PL = min(high_PL, pll_params->max_PL); high_PL = max(high_PL, pll_params->min_PL); low_PL = min_vco_f / target_vco_f; low_PL = min(low_PL, pll_params->max_PL); low_PL = max(low_PL, pll_params->min_PL); /* Find Indices of high_PL and low_PL */ for (pl = 0; pl < 14; pl++) { if (pl_to_div[pl] >= low_PL) { low_PL = pl; break; } } for (pl = 0; pl < 14; pl++) { if (pl_to_div[pl] >= high_PL) { high_PL = pl; break; } } nvhost_dbg_info("low_PL %d(div%d), high_PL %d(div%d)", low_PL, pl_to_div[low_PL], high_PL, pl_to_div[high_PL]); for (pl = low_PL; pl <= high_PL; pl++) { target_vco_f = target_clk_f * pl_to_div[pl]; for (m = pll_params->min_M; m <= pll_params->max_M; m++) { u_f = ref_clk_f / m; if (u_f < pll_params->min_u) break; if (u_f > pll_params->max_u) continue; n = (target_vco_f * m) / ref_clk_f; n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f; if (n > pll_params->max_N) break; for (; n <= n2; n++) { if (n < pll_params->min_N) continue; if (n > pll_params->max_N) break; vco_f = ref_clk_f * n / m; if (vco_f >= min_vco_f && vco_f <= max_vco_f) { lwv = (vco_f + (pl_to_div[pl] / 2)) / pl_to_div[pl]; delta = abs(lwv - target_clk_f); if (delta < best_delta) { best_delta = delta; best_M = m; best_N = n; best_PL = pl; if (best_delta == 0 || /* 0.45% for non best fit */ (!best_fit && (vco_f / best_delta > 218))) { goto found_match; } nvhost_dbg_info("delta %d @ M %d, N %d, PL %d", delta, m, n, pl); } } } } } found_match: BUG_ON(best_delta == ~0); if (best_fit && best_delta != 0) nvhost_dbg_clk("no best match for target @ %dMHz on gpc_pll", target_clk_f); pll->M = best_M; pll->N = best_N; pll->PL = best_PL; /* save current frequency */ pll->freq = ref_clk_f * pll->N / (pll->M * pl_to_div[pll->PL]); *target_freq = pll->freq; nvhost_dbg_clk("actual target freq %d MHz, M %d, N %d, PL %d(div%d)", *target_freq, pll->M, pll->N, pll->PL, pl_to_div[pll->PL]); nvhost_dbg_fn("done"); return 0; }
static int flcn_setup_ucode_image(struct platform_device *dev, u32 *ucode_ptr, const struct firmware *ucode_fw) { struct flcn *v = get_flcn(dev); /* image data is little endian. */ struct ucode_v1_flcn ucode; int w; u32 reserved_offset; u32 tsec_key_offset; nvhost_dbg_fn(""); /* copy the whole thing taking into account endianness */ for (w = 0; w < ucode_fw->size/sizeof(u32); w++) ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]); ucode.bin_header = (struct ucode_bin_header_v1_flcn *)ucode_ptr; /* endian problems would show up right here */ if (ucode.bin_header->bin_magic != 0x10de) { dev_err(&dev->dev, "failed to get firmware magic"); return -EINVAL; } if (ucode.bin_header->bin_ver != 1) { dev_err(&dev->dev, "unsupported firmware version"); return -ENOENT; } /* shouldn't be bigger than what firmware thinks */ if (ucode.bin_header->bin_size > ucode_fw->size) { dev_err(&dev->dev, "ucode image size inconsistency"); return -EINVAL; } nvhost_dbg_info("ucode bin header: magic:0x%x ver:%d size:%d", ucode.bin_header->bin_magic, ucode.bin_header->bin_ver, ucode.bin_header->bin_size); nvhost_dbg_info("ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d", ucode.bin_header->os_bin_header_offset, ucode.bin_header->os_bin_data_offset, ucode.bin_header->os_bin_size); nvhost_dbg_info("ucode bin header: fce bin (header,data) offset size: 0x%x, 0x%x %d", ucode.bin_header->fce_bin_header_offset, ucode.bin_header->fce_bin_data_offset, ucode.bin_header->fce_bin_size); ucode.os_header = (struct ucode_os_header_v1_flcn *) (((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset); nvhost_dbg_info("os ucode header: os code (offset,size): 0x%x, 0x%x", ucode.os_header->os_code_offset, ucode.os_header->os_code_size); nvhost_dbg_info("os ucode header: os data (offset,size): 0x%x, 0x%x", ucode.os_header->os_data_offset, ucode.os_header->os_data_size); nvhost_dbg_info("os ucode header: num apps: %d", ucode.os_header->num_apps); if (ucode.bin_header->fce_bin_header_offset != 0xa5a5a5a5) { ucode.fce_header = (struct ucode_fce_header_v1_flcn *) (((void *)ucode_ptr) + ucode.bin_header->fce_bin_header_offset); nvhost_dbg_info("fce ucode header: offset, buffer_size, size: 0x%x 0x%x 0x%x", ucode.fce_header->fce_ucode_offset, ucode.fce_header->fce_ucode_buffer_size, ucode.fce_header->fce_ucode_size); v->fce.size = ucode.fce_header->fce_ucode_size; v->fce.data_offset = ucode.bin_header->fce_bin_data_offset; } /* make space for reserved area - we need 20 bytes, but we move 256 * bytes because firmware needs to be 256 byte aligned */ reserved_offset = ucode.bin_header->os_bin_data_offset; memmove(((void *)ucode_ptr) + reserved_offset + FALCON_RESERVE, ((void *)ucode_ptr) + reserved_offset, ucode.bin_header->os_bin_size); ucode.bin_header->os_bin_data_offset += FALCON_RESERVE; /* clear 256 bytes before ucode os code */ memset(((void *)ucode_ptr) + reserved_offset, 0, FALCON_RESERVE); /* Copy key to be the 16 bytes before the firmware */ tsec_key_offset = reserved_offset + TSEC_KEY_OFFSET; memcpy(((void *)ucode_ptr) + tsec_key_offset, otf_key, TSEC_KEY_LENGTH); v->os.size = ucode.bin_header->os_bin_size; v->os.bin_data_offset = ucode.bin_header->os_bin_data_offset; v->os.code_offset = ucode.os_header->os_code_offset; v->os.data_offset = ucode.os_header->os_data_offset; v->os.data_size = ucode.os_header->os_data_size; return 0; }