/* * Copyright 2016 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ #include "ppatomfwctrl.h" #include "atomfirmware.h" #include "pp_debug.h" static const union atom_voltage_object_v4 *pp_atomfwctrl_lookup_voltage_type_v4( const struct atom_voltage_objects_info_v4_1 *voltage_object_info_table, uint8_t voltage_type, uint8_t voltage_mode) { unsigned int size = le16_to_cpu( voltage_object_info_table->table_header.structuresize); unsigned int offset = offsetof(struct atom_voltage_objects_info_v4_1, voltage_object[0]); unsigned long start = (unsigned long)voltage_object_info_table; while (offset < size) { const union atom_voltage_object_v4 *voltage_object = (const union atom_voltage_object_v4 *)(start + offset); if (voltage_type == voltage_object->gpio_voltage_obj.header.voltage_type && voltage_mode == voltage_object->gpio_voltage_obj.header.voltage_mode) return voltage_object; offset += le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size); } return NULL; } static struct atom_voltage_objects_info_v4_1 *pp_atomfwctrl_get_voltage_info_table( struct pp_hwmgr *hwmgr) { const void *table_address; uint16_t idx; idx = GetIndexIntoMasterDataTable(voltageobject_info); table_address = cgs_atom_get_data_table(hwmgr->device, idx, NULL, NULL, NULL); PP_ASSERT_WITH_CODE( table_address, "Error retrieving BIOS Table Address!", return NULL); return (struct atom_voltage_objects_info_v4_1 *)table_address; } /** * Returns TRUE if the given voltage type is controlled by GPIO pins. * voltage_type is one of SET_VOLTAGE_TYPE_ASIC_VDDC, SET_VOLTAGE_TYPE_ASIC_MVDDC, SET_VOLTAGE_TYPE_ASIC_MVDDQ. * voltage_mode is one of ATOM_SET_VOLTAGE, ATOM_SET_VOLTAGE_PHASE */ bool pp_atomfwctrl_is_voltage_controlled_by_gpio_v4(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint8_t voltage_mode) { struct atom_voltage_objects_info_v4_1 *voltage_info = (struct atom_voltage_objects_info_v4_1 *) pp_atomfwctrl_get_voltage_info_table(hwmgr); bool ret; /* If we cannot find the table do NOT try to control this voltage. */ PP_ASSERT_WITH_CODE(voltage_info, "Could not find Voltage Table in BIOS.", return false); ret = (pp_atomfwctrl_lookup_voltage_type_v4(voltage_info, voltage_type, voltage_mode)) ? true : false; return ret; } int pp_atomfwctrl_get_voltage_table_v4(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint8_t voltage_mode, struct pp_atomfwctrl_voltage_table *voltage_table) { struct atom_voltage_objects_info_v4_1 *voltage_info = (struct atom_voltage_objects_info_v4_1 *) pp_atomfwctrl_get_voltage_info_table(hwmgr); const union atom_voltage_object_v4 *voltage_object; unsigned int i; int result = 0; PP_ASSERT_WITH_CODE(voltage_info, "Could not find Voltage Table in BIOS.", return -1); voltage_object = pp_atomfwctrl_lookup_voltage_type_v4(voltage_info, voltage_type, voltage_mode); if (!voltage_object) return -1; voltage_table->count = 0; if (voltage_mode == VOLTAGE_OBJ_GPIO_LUT) { PP_ASSERT_WITH_CODE( (voltage_object->gpio_voltage_obj.gpio_entry_num <= PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES), "Too many voltage entries!", result = -1); if (!result) { for (i = 0; i < voltage_object->gpio_voltage_obj. gpio_entry_num; i++) { voltage_table->entries[i].value = le16_to_cpu(voltage_object->gpio_voltage_obj. voltage_gpio_lut[i].voltage_level_mv); voltage_table->entries[i].smio_low = le32_to_cpu(voltage_object->gpio_voltage_obj. voltage_gpio_lut[i].voltage_gpio_reg_val); } voltage_table->count = voltage_object->gpio_voltage_obj.gpio_entry_num; voltage_table->mask_low = le32_to_cpu( voltage_object->gpio_voltage_obj.gpio_mask_val); voltage_table->phase_delay = voltage_object->gpio_voltage_obj.phase_delay_us; } } else if (voltage_mode == VOLTAGE_OBJ_SVID2) { voltage_table->psi1_enable = voltage_object->svid2_voltage_obj.loadline_psi1 & 0x1; voltage_table->psi0_enable = voltage_object->svid2_voltage_obj.psi0_enable & 0x1; voltage_table->max_vid_step = voltage_object->svid2_voltage_obj.maxvstep; voltage_table->telemetry_offset = voltage_object->svid2_voltage_obj.telemetry_offset; voltage_table->telemetry_slope = voltage_object->svid2_voltage_obj.telemetry_gain; } else PP_ASSERT_WITH_CODE(false, "Unsupported Voltage Object Mode!", result = -1); return result; } static struct atom_gpio_pin_lut_v2_1 *pp_atomfwctrl_get_gpio_lookup_table( struct pp_hwmgr *hwmgr) { const void *table_address; uint16_t idx; idx = GetIndexIntoMasterDataTable(gpio_pin_lut); table_address = cgs_atom_get_data_table(hwmgr->device, idx, NULL, NULL, NULL); PP_ASSERT_WITH_CODE(table_address, "Error retrieving BIOS Table Address!", return NULL); return (struct atom_gpio_pin_lut_v2_1 *)table_address; } static bool pp_atomfwctrl_lookup_gpio_pin( struct atom_gpio_pin_lut_v2_1 *gpio_lookup_table, const uint32_t pin_id, struct pp_atomfwctrl_gpio_pin_assignment *gpio_pin_assignment) { unsigned int size = le16_to_cpu( gpio_lookup_table->table_header.structuresize); unsigned int offset = offsetof(struct atom_gpio_pin_lut_v2_1, gpio_pin[0]); unsigned long start = (unsigned long)gpio_lookup_table; while (offset < size) { const struct atom_gpio_pin_assignment *pin_assignment = (const struct atom_gpio_pin_assignment *)(start + offset); if (pin_id == pin_assignment->gpio_id) { gpio_pin_assignment->uc_gpio_pin_bit_shift = pin_assignment->gpio_bitshift; gpio_pin_assignment->us_gpio_pin_aindex = le16_to_cpu(pin_assignment->data_a_reg_index); return true; } offset += offsetof(struct atom_gpio_pin_assignment, gpio_id) + 1; } return false; } /** * Returns TRUE if the given pin id find in lookup table. */ bool pp_atomfwctrl_get_pp_assign_pin(struct pp_hwmgr *hwmgr, const uint32_t pin_id, struct pp_atomfwctrl_gpio_pin_assignment *gpio_pin_assignment) { bool ret = false; struct atom_gpio_pin_lut_v2_1 *gpio_lookup_table = pp_atomfwctrl_get_gpio_lookup_table(hwmgr); /* If we cannot find the table do NOT try to control this voltage. */ PP_ASSERT_WITH_CODE(gpio_lookup_table, "Could not find GPIO lookup Table in BIOS.", return false); ret = pp_atomfwctrl_lookup_gpio_pin(gpio_lookup_table, pin_id, gpio_pin_assignment); return ret; } /** * Enter to SelfRefresh mode. * @param hwmgr */ int pp_atomfwctrl_enter_self_refresh(struct pp_hwmgr *hwmgr) { /* 0 - no action * 1 - leave power to video memory always on */ return 0; } /** pp_atomfwctrl_get_gpu_pll_dividers_vega10(). * * @param hwmgr input parameter: pointer to HwMgr * @param clock_type input parameter: Clock type: 1 - GFXCLK, 2 - UCLK, 0 - All other clocks * @param clock_value input parameter: Clock * @param dividers output parameter:Clock dividers */ int pp_atomfwctrl_get_gpu_pll_dividers_vega10(struct pp_hwmgr *hwmgr, uint32_t clock_type, uint32_t clock_value, struct pp_atomfwctrl_clock_dividers_soc15 *dividers) { struct compute_gpu_clock_input_parameter_v1_8 pll_parameters; struct compute_gpu_clock_output_parameter_v1_8 *pll_output; int result; uint32_t idx; pll_parameters.gpuclock_10khz = (uint32_t)clock_value; pll_parameters.gpu_clock_type = clock_type; idx = GetIndexIntoMasterCmdTable(computegpuclockparam); result = cgs_atom_exec_cmd_table(hwmgr->device, idx, &pll_parameters); if (!result) { pll_output = (struct compute_gpu_clock_output_parameter_v1_8 *) &pll_parameters; dividers->ulClock = le32_to_cpu(pll_output->gpuclock_10khz); dividers->ulDid = le32_to_cpu(pll_output->dfs_did); dividers->ulPll_fb_mult = le32_to_cpu(pll_output->pll_fb_mult); dividers->ulPll_ss_fbsmult = le32_to_cpu(pll_output->pll_ss_fbsmult); dividers->usPll_ss_slew_frac = le16_to_cpu(pll_output->pll_ss_slew_frac); dividers->ucPll_ss_enable = pll_output->pll_ss_enable; } return result; } int pp_atomfwctrl_get_avfs_information(struct pp_hwmgr *hwmgr, struct pp_atomfwctrl_avfs_parameters *param) { uint16_t idx; struct atom_asic_profiling_info_v4_1 *profile; idx = GetIndexIntoMasterDataTable(asic_profiling_info); profile = (struct atom_asic_profiling_info_v4_1 *) cgs_atom_get_data_table(hwmgr->device, idx, NULL, NULL, NULL); if (!profile) return -1; param->ulMaxVddc = le32_to_cpu(profile->maxvddc); param->ulMinVddc = le32_to_cpu(profile->minvddc); param->ulMeanNsigmaAcontant0 = le32_to_cpu(profile->avfs_meannsigma_acontant0); param->ulMeanNsigmaAcontant1 = le32_to_cpu(profile->avfs_meannsigma_acontant1); param->ulMeanNsigmaAcontant2 = le32_to_cpu(profile->avfs_meannsigma_acontant2); param->usMeanNsigmaDcTolSigma = le16_to_cpu(profile->avfs_meannsigma_dc_tol_sigma); param->usMeanNsigmaPlatformMean = le16_to_cpu(profile->avfs_meannsigma_platform_mean); param->usMeanNsigmaPlatformSigma = le16_to_cpu(profile->avfs_meannsigma_platform_sigma); param->ulGbVdroopTableCksoffA0 = le32_to_cpu(profile->gb_vdroop_table_cksoff_a0); param->ulGbVdroopTableCksoffA1 = le32_to_cpu(profile->gb_vdroop_table_cksoff_a1); param->ulGbVdroopTableCksoffA2 = le32_to_cpu(profile->gb_vdroop_table_cksoff_a2); param->ulGbVdroopTableCksonA0 = le32_to_cpu(profile->gb_vdroop_table_ckson_a0); param->ulGbVdroopTableCksonA1 = le32_to_cpu(profile->gb_vdroop_table_ckson_a1); param->ulGbVdroopTableCksonA2 = le32_to_cpu(profile->gb_vdroop_table_ckson_a2); param->ulGbFuseTableCksoffM1 = le32_to_cpu(profile->avfsgb_fuse_table_cksoff_m1); param->usGbFuseTableCksoffM2 = le16_to_cpu(profile->avfsgb_fuse_table_cksoff_m2); param->ulGbFuseTableCksoffB = le32_to_cpu(profile->avfsgb_fuse_table_cksoff_b); param->ulGbFuseTableCksonM1 = le32_to_cpu(profile->avfsgb_fuse_table_ckson_m1); param->usGbFuseTableCksonM2 = le16_to_cpu(profile->avfsgb_fuse_table_ckson_m2); param->ulGbFuseTableCksonB = le32_to_cpu(profile->avfsgb_fuse_table_ckson_b); param->usMaxVoltage025mv = le16_to_cpu(profile->max_voltage_0_25mv); param->ucEnableGbVdroopTableCksoff = profile->enable_gb_vdroop_table_cksoff; param->ucEnableGbVdroopTableCkson = profile->enable_gb_vdroop_table_ckson; param->ucEnableGbFuseTableCksoff = profile->enable_gb_fuse_table_cksoff; param->ucEnableGbFuseTableCkson = profile->enable_gb_fuse_table_ckson; param->usPsmAgeComfactor = le16_to_cpu(profile->psm_age_comfactor); param->ucEnableApplyAvfsCksoffVoltage = profile->enable_apply_avfs_cksoff_voltage; param->ulDispclk2GfxclkM1 = le32_to_cpu(profile->dispclk2gfxclk_a); param->usDispclk2GfxclkM2 = le16_to_cpu(profile->dispclk2gfxclk_b); param->ulDispclk2GfxclkB = le32_to_cpu(profile->dispclk2gfxclk_c); param->ulDcefclk2GfxclkM1 = le32_to_cpu(profile->dcefclk2gfxclk_a); param->usDcefclk2GfxclkM2 = le16_to_cpu(profile->dcefclk2gfxclk_b); param->ulDcefclk2GfxclkB = le32_to_cpu(profile->dcefclk2gfxclk_c); param->ulPixelclk2GfxclkM1 = le32_to_cpu(profile->pixclk2gfxclk_a); param->usPixelclk2GfxclkM2 = le16_to_cpu(profile->pixclk2gfxclk_b); param->ulPixelclk2GfxclkB = le32_to_cpu(profile->pixclk2gfxclk_c); param->ulPhyclk2GfxclkM1 = le32_to_cpu(profile->phyclk2gfxclk_a); param->usPhyclk2GfxclkM2 = le16_to_cpu(profile->phyclk2gfxclk_b); param->ulPhyclk2GfxclkB = le32_to_cpu(profile->phyclk2gfxclk_c); return 0; } int pp_atomfwctrl_get_gpio_information(struct pp_hwmgr *hwmgr, struct pp_atomfwctrl_gpio_parameters *param) { struct atom_smu_info_v3_1 *info; uint16_t idx; idx = GetIndexIntoMasterDataTable(smu_info); info = (struct atom_smu_info_v3_1 *) cgs_atom_get_data_table(hwmgr->device, idx, NULL, NULL, NULL); if (!info) { pr_info("Error retrieving BIOS smu_info Table Address!"); return -1; } param->ucAcDcGpio = info->ac_dc_gpio_bit; param->ucAcDcPolarity = info->ac_dc_polarity; param->ucVR0HotGpio = info->vr0hot_gpio_bit; param->ucVR0HotPolarity = info->vr0hot_polarity; param->ucVR1HotGpio = info->vr1hot_gpio_bit; param->ucVR1HotPolarity = info->vr1hot_polarity; param->ucFwCtfGpio = info->fw_ctf_gpio_bit; param->ucFwCtfPolarity = info->fw_ctf_polarity; return 0; }