Loading drivers/scsi/ufs/debugfs.c +120 −15 Original line number Diff line number Diff line Loading @@ -642,28 +642,124 @@ static int ufsdbg_power_mode_show(struct seq_file *file, void *data) static bool ufsdbg_power_mode_validate(struct ufs_pa_layer_attr *pwr_mode) { if (pwr_mode->gear_rx < UFS_PWM_G1 || pwr_mode->gear_rx > UFS_PWM_G7 || pwr_mode->gear_tx < UFS_PWM_G1 || pwr_mode->gear_tx > UFS_PWM_G7 || pwr_mode->lane_rx < 1 || pwr_mode->lane_rx > 2 || pwr_mode->gear_tx < UFS_PWM_G1 || pwr_mode->gear_tx > UFS_PWM_G7 || pwr_mode->lane_rx < 1 || pwr_mode->lane_rx > 2 || pwr_mode->lane_tx < 1 || pwr_mode->lane_tx > 2 || (pwr_mode->pwr_rx != FAST_MODE && pwr_mode->pwr_rx != SLOW_MODE && (pwr_mode->pwr_rx != FAST_MODE && pwr_mode->pwr_rx != SLOW_MODE && pwr_mode->pwr_rx != FASTAUTO_MODE && pwr_mode->pwr_rx != SLOWAUTO_MODE) || (pwr_mode->pwr_tx != FAST_MODE && pwr_mode->pwr_tx != SLOW_MODE && (pwr_mode->pwr_tx != FAST_MODE && pwr_mode->pwr_tx != SLOW_MODE && pwr_mode->pwr_tx != FASTAUTO_MODE && pwr_mode->pwr_tx != SLOWAUTO_MODE)) pwr_mode->pwr_tx != SLOWAUTO_MODE)) { pr_err("%s: power parameters are not valid\n", __func__); return false; } return true; } static int ufsdbg_cfg_pwr_param(struct ufs_hba *hba, struct ufs_pa_layer_attr *new_pwr, struct ufs_pa_layer_attr *final_pwr) { int ret = 0; bool is_dev_sup_hs = false; bool is_new_pwr_hs = false; int dev_pwm_max_rx_gear; int dev_pwm_max_tx_gear; if (!hba->max_pwr_info.is_valid) { dev_err(hba->dev, "%s: device max power is not valid. can't configure power\n", __func__); return -EINVAL; } if (hba->max_pwr_info.info.pwr_rx == FAST_MODE) is_dev_sup_hs = true; if (new_pwr->pwr_rx == FAST_MODE || new_pwr->pwr_rx == FASTAUTO_MODE) is_new_pwr_hs = true; final_pwr->lane_rx = hba->max_pwr_info.info.lane_rx; final_pwr->lane_tx = hba->max_pwr_info.info.lane_tx; /* device doesn't support HS but requested power is HS */ if (!is_dev_sup_hs && is_new_pwr_hs) { pr_err("%s: device doesn't support HS. requested power is HS\n", __func__); return -ENOTSUPP; } else if ((is_dev_sup_hs && is_new_pwr_hs) || (!is_dev_sup_hs && !is_new_pwr_hs)) { /* * If device and requested power mode are both HS or both PWM * then dev_max->gear_xx are the gears to be assign to * final_pwr->gear_xx */ final_pwr->gear_rx = hba->max_pwr_info.info.gear_rx; final_pwr->gear_tx = hba->max_pwr_info.info.gear_tx; } else if (is_dev_sup_hs && !is_new_pwr_hs) { /* * If device supports HS but requested power is PWM, then we * need to find out what is the max gear in PWM the device * supports */ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &dev_pwm_max_rx_gear); if (!dev_pwm_max_rx_gear) { pr_err("%s: couldn't get device max pwm rx gear\n", __func__); ret = -EINVAL; goto out; } ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &dev_pwm_max_tx_gear); if (!dev_pwm_max_tx_gear) { pr_err("%s: couldn't get device max pwm tx gear\n", __func__); ret = -EINVAL; goto out; } final_pwr->gear_rx = dev_pwm_max_rx_gear; final_pwr->gear_tx = dev_pwm_max_tx_gear; } if ((new_pwr->gear_rx > final_pwr->gear_rx) || (new_pwr->gear_tx > final_pwr->gear_tx) || (new_pwr->lane_rx > final_pwr->lane_rx) || (new_pwr->lane_tx > final_pwr->lane_tx)) { pr_err("%s: (RX,TX) GG,LL: in PWM/HS new pwr [%d%d,%d%d] exceeds device limitation [%d%d,%d%d]\n", __func__, new_pwr->gear_rx, new_pwr->gear_tx, new_pwr->lane_rx, new_pwr->lane_tx, final_pwr->gear_rx, final_pwr->gear_tx, final_pwr->lane_rx, final_pwr->lane_tx); return -ENOTSUPP; } final_pwr->gear_rx = new_pwr->gear_rx; final_pwr->gear_tx = new_pwr->gear_tx; final_pwr->lane_rx = new_pwr->lane_rx; final_pwr->lane_tx = new_pwr->lane_tx; final_pwr->pwr_rx = new_pwr->pwr_rx; final_pwr->pwr_tx = new_pwr->pwr_tx; final_pwr->hs_rate = new_pwr->hs_rate; out: return ret; } static ssize_t ufsdbg_power_mode_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct ufs_hba *hba = file->f_mapping->host->i_private; struct ufs_pa_layer_attr pwr_mode; struct ufs_pa_layer_attr final_pwr_mode; char pwr_mode_str[BUFF_LINE_CAPACITY] = {0}; loff_t buff_pos = 0; int ret; Loading @@ -679,6 +775,7 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, pwr_mode.lane_tx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_rx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_tx = pwr_mode_str[idx++] - '0'; /* * Switching between rates is not currently supported so use the * current rate. Loading @@ -690,16 +787,24 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, if (!ufsdbg_power_mode_validate(&pwr_mode)) return -EINVAL; pr_debug( "%s: new power mode requested [RX,TX]: Gear=[%d,%d] Lanes=[%d,%d], Mode=[%d,%d]\n", __func__, pwr_mode.gear_rx, pwr_mode.gear_tx, pwr_mode.lane_rx, pr_debug("%s: new power mode requested [RX,TX]: Gear=[%d,%d], Lane=[%d,%d], Mode=[%d,%d]\n", __func__, pwr_mode.gear_rx, pwr_mode.gear_tx, pwr_mode.lane_rx, pwr_mode.lane_tx, pwr_mode.pwr_rx, pwr_mode.pwr_tx); ret = ufsdbg_cfg_pwr_param(hba, &pwr_mode, &final_pwr_mode); if (ret) { dev_err(hba->dev, "%s: failed to configure new power parameters, ret = %d\n", __func__, ret); return cnt; } pm_runtime_get_sync(hba->dev); scsi_block_requests(hba->host); ret = ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US); if (!ret) ret = ufshcd_config_pwr_mode(hba, &pwr_mode); ret = ufshcd_change_power_mode(hba, &final_pwr_mode); scsi_unblock_requests(hba->host); pm_runtime_put_sync(hba->dev); if (ret == -EBUSY) Loading drivers/scsi/ufs/ufs-qcom.c +76 −159 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ #include <linux/phy/phy-qcom-ufs.h> #include "ufshci.h" static int ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result); static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result); static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, const char *speed_mode); static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); Loading Loading @@ -542,157 +542,86 @@ struct ufs_qcom_dev_params { u32 desired_working_mode; }; /** * as every power mode, according to the UFS spec, have a defined * number that are not corresponed to their order or power * consumption (i.e 5, 2, 4, 1 respectively from low to high), * we need to map them into array, so we can scan it easily * in order to find the minimum required power mode. * also, we can use this routine to go the other way around, * and from array index, the fetch the correspond power mode. */ static int map_unmap_pwr_mode(u32 mode, bool is_pwr_to_arr) { enum {SL_MD = 0, SLA_MD = 1, FS_MD = 2, FSA_MD = 3, UNDEF = 4}; int ret = -EINVAL; if (is_pwr_to_arr) { switch (mode) { case SLOW_MODE: ret = SL_MD; break; case SLOWAUTO_MODE: ret = SLA_MD; break; case FAST_MODE: ret = FS_MD; break; case FASTAUTO_MODE: ret = FSA_MD; break; default: ret = UNDEF; break; } } else { switch (mode) { case SL_MD: ret = SLOW_MODE; break; case SLA_MD: ret = SLOWAUTO_MODE; break; case FS_MD: ret = FAST_MODE; break; case FSA_MD: ret = FASTAUTO_MODE; break; default: ret = -EINVAL; break; } } return ret; } #define NUM_OF_SUPPORTED_MODES 5 static int get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, struct ufs_pa_layer_attr *dev_max, struct ufs_pa_layer_attr *dev_req) struct ufs_pa_layer_attr *agreed_pwr) { int arr[NUM_OF_SUPPORTED_MODES] = {0}; int i; int min_power; int min_qcom_gear; int min_dev_gear; bool is_max_dev_hs; bool is_max_qcom_hs; bool is_dev_sup_hs = false; bool is_qcom_max_hs = false; /** * mapping the max. supported power mode of the device * and the max. pre-defined support power mode of the vendor * in order to scan them easily */ arr[map_unmap_pwr_mode(dev_max->pwr_rx, true)]++; arr[map_unmap_pwr_mode(dev_max->pwr_tx, true)]++; if (dev_max->pwr_rx == FAST_MODE) is_dev_sup_hs = true; if (qcom_param->desired_working_mode == SLOW) { arr[map_unmap_pwr_mode(qcom_param->rx_pwr_pwm, true)]++; arr[map_unmap_pwr_mode(qcom_param->tx_pwr_pwm, true)]++; if (qcom_param->desired_working_mode == FAST) { is_qcom_max_hs = true; min_qcom_gear = min_t(u32, qcom_param->hs_rx_gear, qcom_param->hs_tx_gear); } else { arr[map_unmap_pwr_mode(qcom_param->rx_pwr_hs, true)]++; arr[map_unmap_pwr_mode(qcom_param->tx_pwr_hs, true)]++; } for (i = 0; i < NUM_OF_SUPPORTED_MODES; ++i) { if (arr[i] != 0) break; min_qcom_gear = min_t(u32, qcom_param->pwm_rx_gear, qcom_param->pwm_tx_gear); } /* no supported power mode found */ if (i == NUM_OF_SUPPORTED_MODES) { return -EINVAL; /* * device doesn't support HS but qcom_param->desired_working_mode is * HS, thus device and qcom_param don't agree */ if (!is_dev_sup_hs && is_qcom_max_hs) { pr_err("%s: failed to agree on power mode (device doesn't support HS but requested power is HS)\n", __func__); return -ENOTSUPP; } else if (is_dev_sup_hs && is_qcom_max_hs) { /* * since device supports HS, it supports FAST_MODE. * since qcom_param->desired_working_mode is also HS * then final decision (FAST/FASTAUTO) is done according * to qcom_params as it is the restricting factor */ agreed_pwr->pwr_rx = agreed_pwr->pwr_tx = qcom_param->rx_pwr_hs; } else { min_power = map_unmap_pwr_mode(i, false); if (min_power >= 0) dev_req->pwr_rx = dev_req->pwr_tx = min_power; else return -EINVAL; /* * here qcom_param->desired_working_mode is PWM. * it doesn't matter whether device supports HS or PWM, * in both cases qcom_param->desired_working_mode will * determine the mode */ agreed_pwr->pwr_rx = agreed_pwr->pwr_tx = qcom_param->rx_pwr_pwm; } /** /* * 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. * the same decision will be made for rx */ dev_req->lane_tx = min_t(u32, dev_max->lane_tx, qcom_param->tx_lanes); dev_req->lane_rx = min_t(u32, dev_max->lane_rx, qcom_param->rx_lanes); if (dev_max->pwr_rx == SLOW_MODE || dev_max->pwr_rx == SLOWAUTO_MODE) is_max_dev_hs = false; else is_max_dev_hs = true; agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, qcom_param->tx_lanes); agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, qcom_param->rx_lanes); /* setting the device maximum gear */ /* 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); /** * setting the desired gear to be the minimum according to the desired * power mode */ if (qcom_param->desired_working_mode == SLOW) { is_max_qcom_hs = false; min_qcom_gear = min_t(u32, qcom_param->pwm_rx_gear, qcom_param->pwm_tx_gear); } else { is_max_qcom_hs = true; min_qcom_gear = min_t(u32, qcom_param->hs_rx_gear, qcom_param->hs_tx_gear); } /** /* * 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. * 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 the gear, as he is the one that also decided previously what * what is the gear, as it is the one that also decided previously what * pwr the device will be configured to. */ if ((is_max_dev_hs && is_max_qcom_hs) || (!is_max_dev_hs && !is_max_qcom_hs)) { dev_req->gear_rx = dev_req->gear_tx = if ((is_dev_sup_hs && is_qcom_max_hs) || (!is_dev_sup_hs && !is_qcom_max_hs)) agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_t(u32, min_dev_gear, min_qcom_gear); } else if (!is_max_dev_hs) { dev_req->gear_rx = dev_req->gear_tx = min_dev_gear; } else { dev_req->gear_rx = dev_req->gear_tx = min_qcom_gear; } dev_req->hs_rate = qcom_param->hs_rate; else if (!is_dev_sup_hs) agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_dev_gear; else agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_qcom_gear; agreed_pwr->hs_rate = qcom_param->hs_rate; return 0; } Loading @@ -702,9 +631,7 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) int err = 0; char mode[BUS_VECTOR_NAME_LEN]; err = ufs_qcom_get_speed_mode(&host->dev_req_params, mode); if (err) goto out; ufs_qcom_get_speed_mode(&host->dev_req_params, mode); vote = ufs_qcom_get_bus_vote(host, mode); if (vote >= 0) Loading @@ -712,7 +639,6 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) else err = vote; out: if (err) dev_err(host->hba->dev, "%s: failed %d\n", __func__, err); else Loading Loading @@ -754,7 +680,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, ufs_qcom_cap.desired_working_mode = UFS_QCOM_LIMIT_DESIRED_MODE; ret = get_pwr_dev_param(&ufs_qcom_cap, dev_max_params, ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap, dev_max_params, dev_req_params); if (ret) { pr_err("%s: failed to determine capabilities\n", Loading Loading @@ -871,13 +798,11 @@ out: return err; } static int ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) { int err = 0; int gear = max_t(u32, p->gear_rx, p->gear_tx); int lanes = max_t(u32, p->lane_rx, p->lane_tx); int pwr = max_t(u32, map_unmap_pwr_mode(p->pwr_rx, true), map_unmap_pwr_mode(p->pwr_tx, true)); int pwr; /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ if (!gear) Loading @@ -887,26 +812,18 @@ static int ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) lanes = 1; if (!p->pwr_rx && !p->pwr_tx) pwr = 0; pwr = map_unmap_pwr_mode(pwr, false); if (pwr < 0) { err = pwr; goto out; } if (pwr == FAST_MODE || pwr == FASTAUTO_MODE) pwr = SLOWAUTO_MODE; else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE || p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) { pwr = FAST_MODE; snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); else p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); } else { pwr = SLOW_MODE; snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", "PWM", gear, lanes); out: return err; } } static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) { Loading drivers/scsi/ufs/ufshcd.c +58 −36 Original line number Diff line number Diff line Loading @@ -2864,8 +2864,8 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) if (hba->max_pwr_info.is_valid) return 0; pwr_info->pwr_tx = FASTAUTO_MODE; pwr_info->pwr_rx = FASTAUTO_MODE; pwr_info->pwr_tx = FAST_MODE; pwr_info->pwr_rx = FAST_MODE; pwr_info->hs_rate = PA_HS_MODE_B; /* Get the connected lane count */ Loading Loading @@ -2896,7 +2896,7 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) __func__, pwr_info->gear_rx); return -EINVAL; } pwr_info->pwr_rx = SLOWAUTO_MODE; pwr_info->pwr_rx = SLOW_MODE; } ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), Loading @@ -2909,29 +2909,29 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) __func__, pwr_info->gear_tx); return -EINVAL; } pwr_info->pwr_tx = SLOWAUTO_MODE; pwr_info->pwr_tx = SLOW_MODE; } hba->max_pwr_info.is_valid = true; return 0; } /** * ufshcd_config_pwr_mode - configure a new power mode * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; int ret; if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, PRE_CHANGE, desired_pwr_mode, &final_params); else memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); /* if already configured to the requested pwr_mode */ if (pwr_mode->gear_rx == hba->pwr_info.gear_rx && pwr_mode->gear_tx == hba->pwr_info.gear_tx && pwr_mode->lane_rx == hba->pwr_info.lane_rx && pwr_mode->lane_tx == hba->pwr_info.lane_tx && pwr_mode->pwr_rx == hba->pwr_info.pwr_rx && pwr_mode->pwr_tx == hba->pwr_info.pwr_tx && pwr_mode->hs_rate == hba->pwr_info.hs_rate) { dev_dbg(hba->dev, "%s: power already configured\n", __func__); return 0; } /* * Configure attributes for power mode change with below. Loading @@ -2939,47 +2939,69 @@ int ufshcd_config_pwr_mode(struct ufs_hba *hba, * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION, * - PA_HSSERIES */ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), final_params.gear_rx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), pwr_mode->gear_rx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), final_params.lane_rx); if (final_params.pwr_rx == FASTAUTO_MODE || final_params.pwr_rx == FAST_MODE) pwr_mode->lane_rx); if (pwr_mode->pwr_rx == FASTAUTO_MODE || pwr_mode->pwr_rx == FAST_MODE) ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE); else ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), FALSE); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), final_params.gear_tx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), pwr_mode->gear_tx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), final_params.lane_tx); if (final_params.pwr_tx == FASTAUTO_MODE || final_params.pwr_tx == FAST_MODE) pwr_mode->lane_tx); if (pwr_mode->pwr_tx == FASTAUTO_MODE || pwr_mode->pwr_tx == FAST_MODE) ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE); else ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), FALSE); if ((final_params.pwr_rx == FASTAUTO_MODE || final_params.pwr_tx == FASTAUTO_MODE || final_params.pwr_rx == FAST_MODE || final_params.pwr_tx == FAST_MODE) && final_params.hs_rate == PA_HS_MODE_B) if (pwr_mode->pwr_rx == FASTAUTO_MODE || pwr_mode->pwr_tx == FASTAUTO_MODE || pwr_mode->pwr_rx == FAST_MODE || pwr_mode->pwr_tx == FAST_MODE) ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), final_params.hs_rate); pwr_mode->hs_rate); ret = ufshcd_uic_change_pwr_mode(hba, pwr_mode->pwr_rx << 4 | pwr_mode->pwr_tx); ret = ufshcd_uic_change_pwr_mode(hba, final_params.pwr_rx << 4 | final_params.pwr_tx); if (ret) { UFSHCD_UPDATE_ERROR_STATS(hba, UFS_ERR_POWER_MODE_CHANGE); dev_err(hba->dev, "pwr_mode: power mode change failed %d\n", ret); "%s: power mode change failed %d\n", __func__, ret); } else { if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, POST_CHANGE, NULL, &final_params); POST_CHANGE, NULL, pwr_mode); memcpy(&hba->pwr_info, &final_params, sizeof(final_params)); memcpy(&hba->pwr_info, pwr_mode, sizeof(struct ufs_pa_layer_attr)); } ufshcd_print_pwr_info(hba); return ret; } /** * ufshcd_config_pwr_mode - configure a new power mode * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; int ret; if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, PRE_CHANGE, desired_pwr_mode, &final_params); else memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); ret = ufshcd_change_power_mode(hba, &final_params); return ret; } Loading include/linux/scsi/ufs/ufshcd.h +2 −2 Original line number Diff line number Diff line Loading @@ -636,8 +636,6 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 Loading Loading @@ -711,4 +709,6 @@ int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode, int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us); int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode); #endif /* End of Header */ Loading
drivers/scsi/ufs/debugfs.c +120 −15 Original line number Diff line number Diff line Loading @@ -642,28 +642,124 @@ static int ufsdbg_power_mode_show(struct seq_file *file, void *data) static bool ufsdbg_power_mode_validate(struct ufs_pa_layer_attr *pwr_mode) { if (pwr_mode->gear_rx < UFS_PWM_G1 || pwr_mode->gear_rx > UFS_PWM_G7 || pwr_mode->gear_tx < UFS_PWM_G1 || pwr_mode->gear_tx > UFS_PWM_G7 || pwr_mode->lane_rx < 1 || pwr_mode->lane_rx > 2 || pwr_mode->gear_tx < UFS_PWM_G1 || pwr_mode->gear_tx > UFS_PWM_G7 || pwr_mode->lane_rx < 1 || pwr_mode->lane_rx > 2 || pwr_mode->lane_tx < 1 || pwr_mode->lane_tx > 2 || (pwr_mode->pwr_rx != FAST_MODE && pwr_mode->pwr_rx != SLOW_MODE && (pwr_mode->pwr_rx != FAST_MODE && pwr_mode->pwr_rx != SLOW_MODE && pwr_mode->pwr_rx != FASTAUTO_MODE && pwr_mode->pwr_rx != SLOWAUTO_MODE) || (pwr_mode->pwr_tx != FAST_MODE && pwr_mode->pwr_tx != SLOW_MODE && (pwr_mode->pwr_tx != FAST_MODE && pwr_mode->pwr_tx != SLOW_MODE && pwr_mode->pwr_tx != FASTAUTO_MODE && pwr_mode->pwr_tx != SLOWAUTO_MODE)) pwr_mode->pwr_tx != SLOWAUTO_MODE)) { pr_err("%s: power parameters are not valid\n", __func__); return false; } return true; } static int ufsdbg_cfg_pwr_param(struct ufs_hba *hba, struct ufs_pa_layer_attr *new_pwr, struct ufs_pa_layer_attr *final_pwr) { int ret = 0; bool is_dev_sup_hs = false; bool is_new_pwr_hs = false; int dev_pwm_max_rx_gear; int dev_pwm_max_tx_gear; if (!hba->max_pwr_info.is_valid) { dev_err(hba->dev, "%s: device max power is not valid. can't configure power\n", __func__); return -EINVAL; } if (hba->max_pwr_info.info.pwr_rx == FAST_MODE) is_dev_sup_hs = true; if (new_pwr->pwr_rx == FAST_MODE || new_pwr->pwr_rx == FASTAUTO_MODE) is_new_pwr_hs = true; final_pwr->lane_rx = hba->max_pwr_info.info.lane_rx; final_pwr->lane_tx = hba->max_pwr_info.info.lane_tx; /* device doesn't support HS but requested power is HS */ if (!is_dev_sup_hs && is_new_pwr_hs) { pr_err("%s: device doesn't support HS. requested power is HS\n", __func__); return -ENOTSUPP; } else if ((is_dev_sup_hs && is_new_pwr_hs) || (!is_dev_sup_hs && !is_new_pwr_hs)) { /* * If device and requested power mode are both HS or both PWM * then dev_max->gear_xx are the gears to be assign to * final_pwr->gear_xx */ final_pwr->gear_rx = hba->max_pwr_info.info.gear_rx; final_pwr->gear_tx = hba->max_pwr_info.info.gear_tx; } else if (is_dev_sup_hs && !is_new_pwr_hs) { /* * If device supports HS but requested power is PWM, then we * need to find out what is the max gear in PWM the device * supports */ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &dev_pwm_max_rx_gear); if (!dev_pwm_max_rx_gear) { pr_err("%s: couldn't get device max pwm rx gear\n", __func__); ret = -EINVAL; goto out; } ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &dev_pwm_max_tx_gear); if (!dev_pwm_max_tx_gear) { pr_err("%s: couldn't get device max pwm tx gear\n", __func__); ret = -EINVAL; goto out; } final_pwr->gear_rx = dev_pwm_max_rx_gear; final_pwr->gear_tx = dev_pwm_max_tx_gear; } if ((new_pwr->gear_rx > final_pwr->gear_rx) || (new_pwr->gear_tx > final_pwr->gear_tx) || (new_pwr->lane_rx > final_pwr->lane_rx) || (new_pwr->lane_tx > final_pwr->lane_tx)) { pr_err("%s: (RX,TX) GG,LL: in PWM/HS new pwr [%d%d,%d%d] exceeds device limitation [%d%d,%d%d]\n", __func__, new_pwr->gear_rx, new_pwr->gear_tx, new_pwr->lane_rx, new_pwr->lane_tx, final_pwr->gear_rx, final_pwr->gear_tx, final_pwr->lane_rx, final_pwr->lane_tx); return -ENOTSUPP; } final_pwr->gear_rx = new_pwr->gear_rx; final_pwr->gear_tx = new_pwr->gear_tx; final_pwr->lane_rx = new_pwr->lane_rx; final_pwr->lane_tx = new_pwr->lane_tx; final_pwr->pwr_rx = new_pwr->pwr_rx; final_pwr->pwr_tx = new_pwr->pwr_tx; final_pwr->hs_rate = new_pwr->hs_rate; out: return ret; } static ssize_t ufsdbg_power_mode_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct ufs_hba *hba = file->f_mapping->host->i_private; struct ufs_pa_layer_attr pwr_mode; struct ufs_pa_layer_attr final_pwr_mode; char pwr_mode_str[BUFF_LINE_CAPACITY] = {0}; loff_t buff_pos = 0; int ret; Loading @@ -679,6 +775,7 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, pwr_mode.lane_tx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_rx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_tx = pwr_mode_str[idx++] - '0'; /* * Switching between rates is not currently supported so use the * current rate. Loading @@ -690,16 +787,24 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, if (!ufsdbg_power_mode_validate(&pwr_mode)) return -EINVAL; pr_debug( "%s: new power mode requested [RX,TX]: Gear=[%d,%d] Lanes=[%d,%d], Mode=[%d,%d]\n", __func__, pwr_mode.gear_rx, pwr_mode.gear_tx, pwr_mode.lane_rx, pr_debug("%s: new power mode requested [RX,TX]: Gear=[%d,%d], Lane=[%d,%d], Mode=[%d,%d]\n", __func__, pwr_mode.gear_rx, pwr_mode.gear_tx, pwr_mode.lane_rx, pwr_mode.lane_tx, pwr_mode.pwr_rx, pwr_mode.pwr_tx); ret = ufsdbg_cfg_pwr_param(hba, &pwr_mode, &final_pwr_mode); if (ret) { dev_err(hba->dev, "%s: failed to configure new power parameters, ret = %d\n", __func__, ret); return cnt; } pm_runtime_get_sync(hba->dev); scsi_block_requests(hba->host); ret = ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US); if (!ret) ret = ufshcd_config_pwr_mode(hba, &pwr_mode); ret = ufshcd_change_power_mode(hba, &final_pwr_mode); scsi_unblock_requests(hba->host); pm_runtime_put_sync(hba->dev); if (ret == -EBUSY) Loading
drivers/scsi/ufs/ufs-qcom.c +76 −159 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ #include <linux/phy/phy-qcom-ufs.h> #include "ufshci.h" static int ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result); static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result); static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, const char *speed_mode); static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); Loading Loading @@ -542,157 +542,86 @@ struct ufs_qcom_dev_params { u32 desired_working_mode; }; /** * as every power mode, according to the UFS spec, have a defined * number that are not corresponed to their order or power * consumption (i.e 5, 2, 4, 1 respectively from low to high), * we need to map them into array, so we can scan it easily * in order to find the minimum required power mode. * also, we can use this routine to go the other way around, * and from array index, the fetch the correspond power mode. */ static int map_unmap_pwr_mode(u32 mode, bool is_pwr_to_arr) { enum {SL_MD = 0, SLA_MD = 1, FS_MD = 2, FSA_MD = 3, UNDEF = 4}; int ret = -EINVAL; if (is_pwr_to_arr) { switch (mode) { case SLOW_MODE: ret = SL_MD; break; case SLOWAUTO_MODE: ret = SLA_MD; break; case FAST_MODE: ret = FS_MD; break; case FASTAUTO_MODE: ret = FSA_MD; break; default: ret = UNDEF; break; } } else { switch (mode) { case SL_MD: ret = SLOW_MODE; break; case SLA_MD: ret = SLOWAUTO_MODE; break; case FS_MD: ret = FAST_MODE; break; case FSA_MD: ret = FASTAUTO_MODE; break; default: ret = -EINVAL; break; } } return ret; } #define NUM_OF_SUPPORTED_MODES 5 static int get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, struct ufs_pa_layer_attr *dev_max, struct ufs_pa_layer_attr *dev_req) struct ufs_pa_layer_attr *agreed_pwr) { int arr[NUM_OF_SUPPORTED_MODES] = {0}; int i; int min_power; int min_qcom_gear; int min_dev_gear; bool is_max_dev_hs; bool is_max_qcom_hs; bool is_dev_sup_hs = false; bool is_qcom_max_hs = false; /** * mapping the max. supported power mode of the device * and the max. pre-defined support power mode of the vendor * in order to scan them easily */ arr[map_unmap_pwr_mode(dev_max->pwr_rx, true)]++; arr[map_unmap_pwr_mode(dev_max->pwr_tx, true)]++; if (dev_max->pwr_rx == FAST_MODE) is_dev_sup_hs = true; if (qcom_param->desired_working_mode == SLOW) { arr[map_unmap_pwr_mode(qcom_param->rx_pwr_pwm, true)]++; arr[map_unmap_pwr_mode(qcom_param->tx_pwr_pwm, true)]++; if (qcom_param->desired_working_mode == FAST) { is_qcom_max_hs = true; min_qcom_gear = min_t(u32, qcom_param->hs_rx_gear, qcom_param->hs_tx_gear); } else { arr[map_unmap_pwr_mode(qcom_param->rx_pwr_hs, true)]++; arr[map_unmap_pwr_mode(qcom_param->tx_pwr_hs, true)]++; } for (i = 0; i < NUM_OF_SUPPORTED_MODES; ++i) { if (arr[i] != 0) break; min_qcom_gear = min_t(u32, qcom_param->pwm_rx_gear, qcom_param->pwm_tx_gear); } /* no supported power mode found */ if (i == NUM_OF_SUPPORTED_MODES) { return -EINVAL; /* * device doesn't support HS but qcom_param->desired_working_mode is * HS, thus device and qcom_param don't agree */ if (!is_dev_sup_hs && is_qcom_max_hs) { pr_err("%s: failed to agree on power mode (device doesn't support HS but requested power is HS)\n", __func__); return -ENOTSUPP; } else if (is_dev_sup_hs && is_qcom_max_hs) { /* * since device supports HS, it supports FAST_MODE. * since qcom_param->desired_working_mode is also HS * then final decision (FAST/FASTAUTO) is done according * to qcom_params as it is the restricting factor */ agreed_pwr->pwr_rx = agreed_pwr->pwr_tx = qcom_param->rx_pwr_hs; } else { min_power = map_unmap_pwr_mode(i, false); if (min_power >= 0) dev_req->pwr_rx = dev_req->pwr_tx = min_power; else return -EINVAL; /* * here qcom_param->desired_working_mode is PWM. * it doesn't matter whether device supports HS or PWM, * in both cases qcom_param->desired_working_mode will * determine the mode */ agreed_pwr->pwr_rx = agreed_pwr->pwr_tx = qcom_param->rx_pwr_pwm; } /** /* * 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. * the same decision will be made for rx */ dev_req->lane_tx = min_t(u32, dev_max->lane_tx, qcom_param->tx_lanes); dev_req->lane_rx = min_t(u32, dev_max->lane_rx, qcom_param->rx_lanes); if (dev_max->pwr_rx == SLOW_MODE || dev_max->pwr_rx == SLOWAUTO_MODE) is_max_dev_hs = false; else is_max_dev_hs = true; agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, qcom_param->tx_lanes); agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, qcom_param->rx_lanes); /* setting the device maximum gear */ /* 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); /** * setting the desired gear to be the minimum according to the desired * power mode */ if (qcom_param->desired_working_mode == SLOW) { is_max_qcom_hs = false; min_qcom_gear = min_t(u32, qcom_param->pwm_rx_gear, qcom_param->pwm_tx_gear); } else { is_max_qcom_hs = true; min_qcom_gear = min_t(u32, qcom_param->hs_rx_gear, qcom_param->hs_tx_gear); } /** /* * 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. * 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 the gear, as he is the one that also decided previously what * what is the gear, as it is the one that also decided previously what * pwr the device will be configured to. */ if ((is_max_dev_hs && is_max_qcom_hs) || (!is_max_dev_hs && !is_max_qcom_hs)) { dev_req->gear_rx = dev_req->gear_tx = if ((is_dev_sup_hs && is_qcom_max_hs) || (!is_dev_sup_hs && !is_qcom_max_hs)) agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_t(u32, min_dev_gear, min_qcom_gear); } else if (!is_max_dev_hs) { dev_req->gear_rx = dev_req->gear_tx = min_dev_gear; } else { dev_req->gear_rx = dev_req->gear_tx = min_qcom_gear; } dev_req->hs_rate = qcom_param->hs_rate; else if (!is_dev_sup_hs) agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_dev_gear; else agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_qcom_gear; agreed_pwr->hs_rate = qcom_param->hs_rate; return 0; } Loading @@ -702,9 +631,7 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) int err = 0; char mode[BUS_VECTOR_NAME_LEN]; err = ufs_qcom_get_speed_mode(&host->dev_req_params, mode); if (err) goto out; ufs_qcom_get_speed_mode(&host->dev_req_params, mode); vote = ufs_qcom_get_bus_vote(host, mode); if (vote >= 0) Loading @@ -712,7 +639,6 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) else err = vote; out: if (err) dev_err(host->hba->dev, "%s: failed %d\n", __func__, err); else Loading Loading @@ -754,7 +680,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, ufs_qcom_cap.desired_working_mode = UFS_QCOM_LIMIT_DESIRED_MODE; ret = get_pwr_dev_param(&ufs_qcom_cap, dev_max_params, ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap, dev_max_params, dev_req_params); if (ret) { pr_err("%s: failed to determine capabilities\n", Loading Loading @@ -871,13 +798,11 @@ out: return err; } static int ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) { int err = 0; int gear = max_t(u32, p->gear_rx, p->gear_tx); int lanes = max_t(u32, p->lane_rx, p->lane_tx); int pwr = max_t(u32, map_unmap_pwr_mode(p->pwr_rx, true), map_unmap_pwr_mode(p->pwr_tx, true)); int pwr; /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ if (!gear) Loading @@ -887,26 +812,18 @@ static int ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) lanes = 1; if (!p->pwr_rx && !p->pwr_tx) pwr = 0; pwr = map_unmap_pwr_mode(pwr, false); if (pwr < 0) { err = pwr; goto out; } if (pwr == FAST_MODE || pwr == FASTAUTO_MODE) pwr = SLOWAUTO_MODE; else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE || p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) { pwr = FAST_MODE; snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); else p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); } else { pwr = SLOW_MODE; snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", "PWM", gear, lanes); out: return err; } } static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) { Loading
drivers/scsi/ufs/ufshcd.c +58 −36 Original line number Diff line number Diff line Loading @@ -2864,8 +2864,8 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) if (hba->max_pwr_info.is_valid) return 0; pwr_info->pwr_tx = FASTAUTO_MODE; pwr_info->pwr_rx = FASTAUTO_MODE; pwr_info->pwr_tx = FAST_MODE; pwr_info->pwr_rx = FAST_MODE; pwr_info->hs_rate = PA_HS_MODE_B; /* Get the connected lane count */ Loading Loading @@ -2896,7 +2896,7 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) __func__, pwr_info->gear_rx); return -EINVAL; } pwr_info->pwr_rx = SLOWAUTO_MODE; pwr_info->pwr_rx = SLOW_MODE; } ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), Loading @@ -2909,29 +2909,29 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) __func__, pwr_info->gear_tx); return -EINVAL; } pwr_info->pwr_tx = SLOWAUTO_MODE; pwr_info->pwr_tx = SLOW_MODE; } hba->max_pwr_info.is_valid = true; return 0; } /** * ufshcd_config_pwr_mode - configure a new power mode * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; int ret; if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, PRE_CHANGE, desired_pwr_mode, &final_params); else memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); /* if already configured to the requested pwr_mode */ if (pwr_mode->gear_rx == hba->pwr_info.gear_rx && pwr_mode->gear_tx == hba->pwr_info.gear_tx && pwr_mode->lane_rx == hba->pwr_info.lane_rx && pwr_mode->lane_tx == hba->pwr_info.lane_tx && pwr_mode->pwr_rx == hba->pwr_info.pwr_rx && pwr_mode->pwr_tx == hba->pwr_info.pwr_tx && pwr_mode->hs_rate == hba->pwr_info.hs_rate) { dev_dbg(hba->dev, "%s: power already configured\n", __func__); return 0; } /* * Configure attributes for power mode change with below. Loading @@ -2939,47 +2939,69 @@ int ufshcd_config_pwr_mode(struct ufs_hba *hba, * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION, * - PA_HSSERIES */ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), final_params.gear_rx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), pwr_mode->gear_rx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), final_params.lane_rx); if (final_params.pwr_rx == FASTAUTO_MODE || final_params.pwr_rx == FAST_MODE) pwr_mode->lane_rx); if (pwr_mode->pwr_rx == FASTAUTO_MODE || pwr_mode->pwr_rx == FAST_MODE) ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE); else ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), FALSE); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), final_params.gear_tx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), pwr_mode->gear_tx); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), final_params.lane_tx); if (final_params.pwr_tx == FASTAUTO_MODE || final_params.pwr_tx == FAST_MODE) pwr_mode->lane_tx); if (pwr_mode->pwr_tx == FASTAUTO_MODE || pwr_mode->pwr_tx == FAST_MODE) ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE); else ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), FALSE); if ((final_params.pwr_rx == FASTAUTO_MODE || final_params.pwr_tx == FASTAUTO_MODE || final_params.pwr_rx == FAST_MODE || final_params.pwr_tx == FAST_MODE) && final_params.hs_rate == PA_HS_MODE_B) if (pwr_mode->pwr_rx == FASTAUTO_MODE || pwr_mode->pwr_tx == FASTAUTO_MODE || pwr_mode->pwr_rx == FAST_MODE || pwr_mode->pwr_tx == FAST_MODE) ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), final_params.hs_rate); pwr_mode->hs_rate); ret = ufshcd_uic_change_pwr_mode(hba, pwr_mode->pwr_rx << 4 | pwr_mode->pwr_tx); ret = ufshcd_uic_change_pwr_mode(hba, final_params.pwr_rx << 4 | final_params.pwr_tx); if (ret) { UFSHCD_UPDATE_ERROR_STATS(hba, UFS_ERR_POWER_MODE_CHANGE); dev_err(hba->dev, "pwr_mode: power mode change failed %d\n", ret); "%s: power mode change failed %d\n", __func__, ret); } else { if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, POST_CHANGE, NULL, &final_params); POST_CHANGE, NULL, pwr_mode); memcpy(&hba->pwr_info, &final_params, sizeof(final_params)); memcpy(&hba->pwr_info, pwr_mode, sizeof(struct ufs_pa_layer_attr)); } ufshcd_print_pwr_info(hba); return ret; } /** * ufshcd_config_pwr_mode - configure a new power mode * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; int ret; if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, PRE_CHANGE, desired_pwr_mode, &final_params); else memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); ret = ufshcd_change_power_mode(hba, &final_params); return ret; } Loading
include/linux/scsi/ufs/ufshcd.h +2 −2 Original line number Diff line number Diff line Loading @@ -636,8 +636,6 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 Loading Loading @@ -711,4 +709,6 @@ int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode, int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us); int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode); #endif /* End of Header */