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

Commit f2b4e6c2 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: Active Power Mode - configuring bActiveICCLevel"

parents e9d3a50e a9043627
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ enum flag_idn {

/* Attribute idn for Query requests */
enum attr_idn {
	QUERY_ATTR_IDN_ACTIVE_ICC_LVL	= 0x03,
	QUERY_ATTR_IDN_BKOPS_STATUS	= 0x05,
	QUERY_ATTR_IDN_EE_CONTROL	= 0x0D,
	QUERY_ATTR_IDN_EE_STATUS	= 0x0E,
@@ -153,6 +154,31 @@ enum unit_desc_param {
	UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1	= 0x22,
};

/* bActiveICCLevel parameter current units */
enum {
	UFSHCD_NANO_AMP		= 0,
	UFSHCD_MICRO_AMP	= 1,
	UFSHCD_MILI_AMP		= 2,
	UFSHCD_AMP		= 3,
};

#define POWER_DESC_MAX_SIZE			0x62
#define POWER_DESC_MAX_ACTV_ICC_LVLS		16

/* Attribute  bActiveICCLevel parameter bit masks definitions */
#define ATTR_ICC_LVL_UNIT_OFFSET	14
#define ATTR_ICC_LVL_UNIT_MASK		(0x3 << ATTR_ICC_LVL_UNIT_OFFSET)
#define ATTR_ICC_LVL_VALUE_MASK		0x3FF

/* Power descriptor parameters offsets in bytes */
enum power_desc_param_offset {
	PWR_DESC_LEN			= 0x0,
	PWR_DESC_TYPE			= 0x1,
	PWR_DESC_ACTIVE_LVLS_VCC_0	= 0x2,
	PWR_DESC_ACTIVE_LVLS_VCCQ_0	= 0x22,
	PWR_DESC_ACTIVE_LVLS_VCCQ2_0	= 0x42,
};

/* Exception event mask values */
enum {
	MASK_EE_STATUS		= 0xFFFF,
+113 −0
Original line number Diff line number Diff line
@@ -3188,6 +3188,118 @@ out:
	return ret;
}

/**
 * ufshcd_get_max_icc_level - calculate the ICC level
 * @sup_curr_uA: max. current supported by the regulator
 * @start_scan: row at the desc table to start scan from
 * @buff: power descriptor buffer
 *
 * Returns calculated max ICC level for specific regulator
 */
static u8 ufshcd_get_max_icc_level(int sup_curr_uA, u8 start_scan, char *buff)
{
	u8 i;
	int curr_uA;
	u16 data;
	u16 unit;

	for (i = start_scan; i >= 0; i--) {
		data = be16_to_cpu(*((u16 *)(buff + 2*i)));
		unit = (data & ATTR_ICC_LVL_UNIT_MASK) >>
						ATTR_ICC_LVL_UNIT_OFFSET;
		curr_uA = data & ATTR_ICC_LVL_VALUE_MASK;
		switch (unit) {
		case UFSHCD_NANO_AMP:
			curr_uA = curr_uA / 1000;
			break;
		case UFSHCD_MILI_AMP:
			curr_uA = curr_uA * 1000;
			break;
		case UFSHCD_AMP:
			curr_uA = curr_uA * 1000 * 1000;
			break;
		case UFSHCD_MICRO_AMP:
		default:
			break;
		}
		if (sup_curr_uA >= curr_uA)
			break;
	}
	return i;
}

/**
 * ufshcd_calc_icc_level - calculate the max ICC level
 * In case regulators are not initialized we'll return 0
 * @hba: per-adapter instance
 * @desc_buf: power descriptor buffer to extract ICC levels from.
 * @len: length of desc_buff
 *
 * Returns calculated ICC level
 */
static u8 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba,
							u8 *desc_buf, int len)
{
	u8 icc_level = 0;

	if (!hba->vreg_info.vcc || !hba->vreg_info.vccq ||
						!hba->vreg_info.vccq2) {
		dev_err(hba->dev,
			"%s: Regulator capability was not set, actvIccLevel=%d",
							__func__, icc_level);
		goto out;
	}

	if (hba->vreg_info.vcc)
		icc_level = ufshcd_get_max_icc_level(
				hba->vreg_info.vcc->max_uA,
				POWER_DESC_MAX_ACTV_ICC_LVLS - 1,
				&desc_buf[PWR_DESC_ACTIVE_LVLS_VCC_0]);

	if (hba->vreg_info.vccq)
		icc_level = ufshcd_get_max_icc_level(
				hba->vreg_info.vccq->max_uA,
				icc_level,
				&desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ_0]);

	if (hba->vreg_info.vccq2)
		icc_level = ufshcd_get_max_icc_level(
				hba->vreg_info.vccq2->max_uA,
				icc_level,
				&desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ2_0]);
out:
	return icc_level;
}

static void ufshcd_init_icc_levels(struct ufs_hba *hba)
{
	u32 icc_level;
	int ret;
	int buff_len = POWER_DESC_MAX_SIZE;
	u8 desc_buf[POWER_DESC_MAX_SIZE];

	ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC,
			QUERY_DESC_IDN_POWER, 0, 0, desc_buf, &buff_len);

	if (ret || (buff_len < POWER_DESC_MAX_SIZE)) {
		dev_err(hba->dev,
			"%s: Failed reading power descriptor.len = %d ret = %d",
			__func__, buff_len, ret);
		return;
	}

	icc_level = ufshcd_find_max_sup_active_icc_level(hba, desc_buf,
								buff_len);

	ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
			QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, &icc_level);

	if (ret)
		dev_err(hba->dev,
			"%s: Failed configuring bActiveICCLevel = %d ret = %d",
			__func__, icc_level , ret);
}

/**
 * ufshcd_async_scan - asynchronous execution for link startup
 * @data: data pointer to pass to this function
@@ -3217,6 +3329,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)

	/* If we are in error handling context no need to scan the host */
	if (!ufshcd_eh_in_progress(hba)) {
		ufshcd_init_icc_levels(hba);
		scsi_scan_host(hba->host);
		pm_runtime_put_sync(hba->dev);
	}