Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f2dd165 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "scsi: ufs: fix bug when changing power mode via debugfs"

parents 28651259 97658959
Loading
Loading
Loading
Loading
+120 −15
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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)
+76 −159
Original line number Diff line number Diff line
@@ -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);
@@ -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;
}

@@ -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)
@@ -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
@@ -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",
@@ -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)
@@ -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)
{
+58 −36
Original line number Diff line number Diff line
@@ -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 */
@@ -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),
@@ -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.
@@ -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;
}
+2 −2
Original line number Diff line number Diff line
@@ -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
@@ -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 */