/** * ufshcd_parse_regulator_info - get regulator info from device tree * @hba: per adapter instance * * Get regulator info from device tree for vcc, vccq, vccq2 power supplies. * If any of the supplies are not defined it is assumed that they are always-on * and hence return zero. If the property is defined but parsing is failed * then return corresponding error. * * Return: 0 upon success; < 0 upon failure.
*/ staticint ufshcd_parse_regulator_info(struct ufs_hba *hba)
{ int err; struct device *dev = hba->dev; struct ufs_vreg_info *info = &hba->vreg_info;
err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba, true); if (err) goto out;
err = ufshcd_populate_vreg(dev, "vcc", &info->vcc, false); if (err) goto out;
err = ufshcd_populate_vreg(dev, "vccq", &info->vccq, false); if (err) goto out;
ret = of_property_read_u32(dev->of_node, "lanes-per-direction",
&hba->lanes_per_direction); if (ret) {
dev_dbg(hba->dev, "%s: failed to read lanes-per-direction, ret=%d\n",
__func__, ret);
hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
}
}
/** * ufshcd_parse_clock_min_max_freq - Parse MIN and MAX clocks freq * @hba: per adapter instance * * This function parses MIN and MAX frequencies of all clocks required * by the host drivers. * * Returns 0 for success and non-zero for failure
*/ staticint ufshcd_parse_clock_min_max_freq(struct ufs_hba *hba)
{ struct list_head *head = &hba->clk_list_head; struct ufs_clk_info *clki; struct dev_pm_opp *opp; unsignedlong freq;
u8 idx = 0;
list_for_each_entry(clki, head, list) { if (!clki->name) continue;
clki->clk = devm_clk_get(hba->dev, clki->name); if (IS_ERR(clki->clk)) continue;
/* Find Max Freq */
freq = ULONG_MAX;
opp = dev_pm_opp_find_freq_floor_indexed(hba->dev, &freq, idx); if (IS_ERR(opp)) {
dev_err(hba->dev, "Failed to find OPP for MAX frequency\n"); return PTR_ERR(opp);
}
clki->max_freq = dev_pm_opp_get_freq_indexed(opp, idx);
dev_pm_opp_put(opp);
/* Find Min Freq */
freq = 0;
opp = dev_pm_opp_find_freq_ceil_indexed(hba->dev, &freq, idx); if (IS_ERR(opp)) {
dev_err(hba->dev, "Failed to find OPP for MIN frequency\n"); return PTR_ERR(opp);
}
clki->min_freq = dev_pm_opp_get_freq_indexed(opp, idx++);
dev_pm_opp_put(opp);
}
/* OPP expects clk_names to be NULL terminated */
clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL); if (!clk_names) return -ENOMEM;
/* * We still need to get reference to all clocks as the UFS core uses * them separately.
*/ for (i = 0; i < cnt; i++) {
ret = of_property_read_string_index(np, "clock-names", i,
&clk_names[i]); if (ret) return ret;
clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); if (!clki) return -ENOMEM;
clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL); if (!clki->name) return -ENOMEM;
if (!strcmp(clk_names[i], "ref_clk"))
clki->keep_link_active = true;
ret = devm_pm_opp_set_config(dev, &config); if (ret) return ret;
ret = devm_pm_opp_of_add_table(dev); if (ret) {
dev_err(dev, "Failed to add OPP table: %d\n", ret); return ret;
}
ret = ufshcd_parse_clock_min_max_freq(hba); if (ret) return ret;
hba->use_pm_opp = true;
return 0;
}
/** * ufshcd_negotiate_pwr_params - find power mode settings that are supported by * both the controller and the device * @host_params: pointer to host parameters * @dev_max: pointer to device attributes * @agreed_pwr: returned agreed attributes * * Return: 0 on success, non-zero value on failure.
*/ int ufshcd_negotiate_pwr_params(conststruct ufs_host_params *host_params, conststruct ufs_pa_layer_attr *dev_max, struct ufs_pa_layer_attr *agreed_pwr)
{ int min_host_gear; int min_dev_gear; bool is_dev_sup_hs = false; bool is_host_max_hs = false;
if (dev_max->pwr_rx == FAST_MODE)
is_dev_sup_hs = true;
/* * device doesn't support HS but host_params->desired_working_mode is HS, * thus device and host_params don't agree
*/ if (!is_dev_sup_hs && is_host_max_hs) {
pr_info("%s: device doesn't support HS\n",
__func__); return -ENOTSUPP;
} elseif (is_dev_sup_hs && is_host_max_hs) { /* * since device supports HS, it supports FAST_MODE. * since host_params->desired_working_mode is also HS * then final decision (FAST/FASTAUTO) is done according * to pltfrm_params as it is the restricting factor
*/
agreed_pwr->pwr_rx = host_params->rx_pwr_hs;
agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
} else { /* * here host_params->desired_working_mode is PWM. * it doesn't matter whether device supports HS or PWM, * in both cases host_params->desired_working_mode will * determine the mode
*/
agreed_pwr->pwr_rx = host_params->rx_pwr_pwm;
agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
}
/* * we would like tx to work in the minimum number of lanes * between device capability and vendor preferences. * the same decision will be made for rx
*/
agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx,
host_params->tx_lanes);
agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx,
host_params->rx_lanes);
/* device maximum gear is the minimum between device rx and tx gears */
min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx);
/* * if both device capabilities and vendor pre-defined preferences are * both HS or both PWM then set the minimum gear to be the chosen * working gear. * if one is PWM and one is HS then the one that is PWM get to decide * what is the gear, as it is the one that also decided previously what * pwr the device will be configured to.
*/ if ((is_dev_sup_hs && is_host_max_hs) ||
(!is_dev_sup_hs && !is_host_max_hs)) {
agreed_pwr->gear_rx =
min_t(u32, min_dev_gear, min_host_gear);
} elseif (!is_dev_sup_hs) {
agreed_pwr->gear_rx = min_dev_gear;
} else {
agreed_pwr->gear_rx = min_host_gear;
}
agreed_pwr->gear_tx = agreed_pwr->gear_rx;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.