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

Commit 92bf9e3e authored by Abinaya P's avatar Abinaya P Committed by Ankit Sharma
Browse files

msm: qpnp-haptic: add additional checks to avoid division by zero.



While calculating the +/-30% variation from the resonant frequency for auto
resonance search algorithm, division by zero is happening causing device
crash. Add additional checks to avoid such crashes. Also, RATE_CFG1 and
RATE_CFG2 registers should be updated with values from AUTO_RES_LO and
AUTO_RES_HI registers respectively only if the frequency variation is
+/-25% of the resonant freqeuncy. Add code change for the same.

CRs-Fixed: 1024697
Change-Id: I2a68cb61331ce4e68857031a4c0c14fded77a0e5
Signed-off-by: default avatarAbinaya P <abinayap@codeaurora.org>
Signed-off-by: default avatarHimanshu Aggarwal <haggarwa@codeaurora.org>
parent cebbcd90
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -66,6 +66,28 @@ Optional properties when qcom,actuator-type is "lra"
			"none", "opt1", "opt2" and "opt3" (default)
 - qcom,lra-res-cal-period : Auto resonance calibration period. The values range from
			4 to 32(default)
 - qcom,perform-lra-auto-resonance-search : boolean, define this property if:
		a) the underlying PMI chip does not have a register in the MISC block to
		read the error percentage in RC clock
		b) the actuator type is LRA
		Defining this causes the auto resonance search algorithm to be be performed
		for such devices.
		c) This property is not defined by default.

- qcom,drive-period-code-max-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will
			be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers
			only if the variation from the resonant frequency is within the value
			mentioned by this property on the higher side.
			The default value is 25, which means if the drive period code resulting
			from AUTO_RES register's is more than 25 percent of the existing drive
			period code, then driver does not update RATE_CFG registers.
- qcom,drive-period-code-min-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will
			be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers
			only if the variation from the resonant frequency is within the value
			mentioned by this property on the lower side.
                        The default value is 25, which means if the drive period code resulting
                        from AUTO_RES register's is less than 25 percent of the existing drive
                        period code, then driver does not update RATE_CFG registers.

Optional properties when qcom,lra-auto-res-mode is "qwd"
 - qcom,time-required-to-generate-back-emf-us: Time period required to generate sufficient
+204 −73
Original line number Diff line number Diff line
@@ -142,8 +142,6 @@
#define QPNP_TEST_TIMER_MS		5

#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN	20000
#define AUTO_RES_ERR_CAPTURE_RES	5
#define AUTO_RES_ERR_MAX		15

#define MISC_TRIM_ERROR_RC19P2_CLK	0x09F5
#define MISC_SEC_ACCESS			0x09D0
@@ -152,8 +150,22 @@

#define POLL_TIME_AUTO_RES_ERR_NS	(5 * NSEC_PER_MSEC)

#define LRA_POS_FREQ_COUNT		6
int lra_play_rate_code[LRA_POS_FREQ_COUNT];
#define MAX_POSITIVE_VARIATION_LRA_FREQ 30
#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30
#define FREQ_VARIATION_STEP		5
#define AUTO_RES_ERROR_CAPTURE_RES	5
#define AUTO_RES_ERROR_MAX		30
#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \
	((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \
	 / FREQ_VARIATION_STEP)
#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \
		(hap->init_drive_period_code = (hap->init_drive_period_code * \
					(1000 + rc_clk_err_percent_x10)) / 1000)
#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \
		(hap->init_drive_period_code = (hap->init_drive_period_code * \
					(1000 - rc_clk_err_percent_x10)) / 1000)

u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE];

/* haptic debug register set */
static u8 qpnp_hap_dbg_regs[] = {
@@ -246,6 +258,15 @@ struct qpnp_pwm_info {
 *  @ pwm_info - pwm info
 *  @ lock - mutex lock
 *  @ wf_lock - mutex lock for waveform
 *  @ init_drive_period_code - the initial lra drive period code
 *  @ drive_period_code_max_limit_percent_variation - maximum limit of
      percentage variation of drive period code
 *  @ drive_period_code_min_limit_percent_variation - minimum limit og
      percentage variation of drive period code
 *  @ drive_period_code_max_limit - calculated drive period code with
      percentage variation on the higher side.
 *  @ drive_period_code_min_limit - calculated drive period code with
      percentage variation on the lower side
 *  @ play_mode - play mode
 *  @ auto_res_mode - auto resonace mode
 *  @ lra_high_z - high z option line
@@ -282,6 +303,8 @@ struct qpnp_pwm_info {
 *  @ sup_brake_pat - support custom brake pattern
 *  @ correct_lra_drive_freq - correct LRA Drive Frequency
 *  @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
 *  @ perform_lra_auto_resonance_search - whether lra auto resonance search
 *    algorithm should be performed or not.
 */
struct qpnp_hap {
	struct platform_device		*pdev;
@@ -302,6 +325,7 @@ struct qpnp_hap {
	enum qpnp_hap_mode		play_mode;
	enum qpnp_hap_auto_res_mode	auto_res_mode;
	enum qpnp_hap_high_z		lra_high_z;
	u32				init_drive_period_code;
	u32				timeout_ms;
	u32				time_required_to_generate_back_emf_us;
	u32				vmax_mv;
@@ -315,6 +339,10 @@ struct qpnp_hap {
	u32				play_irq;
	u32				sc_irq;
	u16				base;
	u16				drive_period_code_max_limit;
	u16				drive_period_code_min_limit;
	u8			drive_period_code_max_limit_percent_variation;
	u8			drive_period_code_min_limit_percent_variation;
	u8				act_type;
	u8				wave_shape;
	u8				wave_samp[QPNP_HAP_WAV_SAMP_LEN];
@@ -336,6 +364,7 @@ struct qpnp_hap {
	bool				sup_brake_pat;
	bool				correct_lra_drive_freq;
	bool				misc_trim_error_rc19p2_clk_reg_present;
	bool				perform_lra_auto_resonance_search;
};

static struct qpnp_hap *ghap;
@@ -1317,29 +1346,62 @@ static struct device_attribute qpnp_hap_attrs[] = {
		qpnp_hap_min_max_test_data_store),
};

static void calculate_lra_code(struct qpnp_hap *hap)
static int calculate_lra_code(struct qpnp_hap *hap)
{
	u8 play_rate_code_lo, play_rate_code_hi;
	int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1;
	int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX;
	u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0;
	u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation;
	u8 start_variation = AUTO_RES_ERROR_MAX, i;
	u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1;
	int rc = 0;

	qpnp_hap_read_reg(hap, &play_rate_code_lo,
	rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo,
			QPNP_HAP_RATE_CFG1_REG(hap->base));
	qpnp_hap_read_reg(hap, &play_rate_code_hi,
	if (rc) {
		dev_err(&hap->pdev->dev,
				"Error while reading RATE_CFG1 register\n");
		return rc;
	}

	rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_hi,
			QPNP_HAP_RATE_CFG2_REG(hap->base));
	if (rc) {
		dev_err(&hap->pdev->dev,
				"Error while reading RATE_CFG2 register\n");
		return rc;
	}

	play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff);
	if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) {
		dev_err(&hap->pdev->dev,
			"Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n");
		return -EINVAL;
	}

	lra_init_freq = 200000 / play_rate_code;
	lra_drive_period_code =
	 (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff);
	lra_drive_frequency_hz = 200000 / lra_drive_period_code;

	while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) {
		freq_variation = (lra_init_freq * start_variation) / 100;
		lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq -
					freq_variation);
		lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq +
					freq_variation);
		start_variation -= AUTO_RES_ERR_CAPTURE_RES;
	while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) {
		freq_variation =
			 (lra_drive_frequency_hz * start_variation) / 100;
		adjusted_lra_play_rate_code[neg_idx++] =
			200000 / (lra_drive_frequency_hz - freq_variation);
		adjusted_lra_play_rate_code[pos_idx--] =
			200000 / (lra_drive_frequency_hz + freq_variation);
		start_variation -= AUTO_RES_ERROR_CAPTURE_RES;
	}

	dev_dbg(&hap->pdev->dev,
		"lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n"
		"lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n"
		"Calculated play rate code values are :\n",
		lra_drive_period_code_lo, lra_drive_period_code_hi,
		lra_drive_period_code, lra_drive_frequency_hz);

	for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i)
		dev_dbg(&hap->pdev->dev,
			 " 0x%x", adjusted_lra_play_rate_code[i]);

	return 0;
}

static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
@@ -1372,13 +1434,31 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
static void update_lra_frequency(struct qpnp_hap *hap)
{
	u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0;
	u32 play_rate_code;

	qpnp_hap_read_reg(hap, &lra_auto_res_lo,
				QPNP_HAP_LRA_AUTO_RES_LO(hap->base));
	qpnp_hap_read_reg(hap, &lra_auto_res_hi,
				QPNP_HAP_LRA_AUTO_RES_HI(hap->base));

	if (lra_auto_res_lo && lra_auto_res_hi) {
	play_rate_code =
		 (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);

	dev_dbg(&hap->pdev->dev,
		"lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
		lra_auto_res_lo, lra_auto_res_hi, play_rate_code);

	/*
	 * If the drive period code read from AUTO RES_LO and AUTO_RES_HI
	 * registers is more than the max limit percent variation read from
	 * DT or less than the min limit percent variation read from DT, then
	 * RATE_CFG registers are not uptdated.
	 */

	if ((play_rate_code <= hap->drive_period_code_min_limit) ||
		(play_rate_code >= hap->drive_period_code_max_limit))
		return;

	qpnp_hap_write_reg(hap, &lra_auto_res_lo,
					QPNP_HAP_RATE_CFG1_REG(hap->base));

@@ -1386,7 +1466,6 @@ static void update_lra_frequency(struct qpnp_hap *hap)
	qpnp_hap_write_reg(hap, &lra_auto_res_hi,
					QPNP_HAP_RATE_CFG2_REG(hap->base));
}
}

static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
{
@@ -1415,36 +1494,38 @@ static void correct_auto_res_error(struct work_struct *auto_res_err_work)
				struct qpnp_hap, auto_res_err_work);

	u8 lra_code_lo, lra_code_hi, disable_hap = 0x00;
	static int lra_freq_index;
	ktime_t currtime, remaining_time;
	int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT;
	static u8 lra_freq_index;
	ktime_t currtime = ktime_set(0, 0), remaining_time = ktime_set(0, 0);

	if (hrtimer_active(&hap->hap_timer)) {
	if (hrtimer_active(&hap->hap_timer))
		remaining_time  = hrtimer_get_remaining(&hap->hap_timer);
		rem = (int)ktime_to_us(remaining_time);
	}

	qpnp_hap_play(hap, 0);
	qpnp_hap_write_reg(hap, &disable_hap,
				QPNP_HAP_EN_CTL_REG(hap->base));

	lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK;
	if (hap->perform_lra_auto_resonance_search) {
		lra_code_lo =
			adjusted_lra_play_rate_code[lra_freq_index]
						& QPNP_HAP_RATE_CFG1_MASK;

		qpnp_hap_write_reg(hap, &lra_code_lo,
					QPNP_HAP_RATE_CFG1_REG(hap->base));

	qpnp_hap_read_reg(hap, &lra_code_hi,
				QPNP_HAP_RATE_CFG2_REG(hap->base));

	lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK;
	temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT;
	lra_code_hi |= temp;
		lra_code_hi = adjusted_lra_play_rate_code[lra_freq_index]
						>> QPNP_HAP_RATE_CFG2_SHFT;

		qpnp_hap_write_reg(hap, &lra_code_hi,
					QPNP_HAP_RATE_CFG2_REG(hap->base));

	lra_freq_index++;
		lra_freq_index = (lra_freq_index+1) %
					ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE;
	}

	dev_dbg(&hap->pdev->dev, "Remaining time is %lld\n",
						ktime_to_us(remaining_time));

	if (rem > 0) {
	if ((ktime_to_us(remaining_time)) > 0) {
		currtime = ktime_get();
		hap->state = 1;
		hrtimer_forward(&hap->hap_timer, currtime, remaining_time);
@@ -1480,8 +1561,9 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
			 * and enable it after the sleep of
			 * 'time_required_to_generate_back_emf_us' is completed.
			 */
			if (hap->correct_lra_drive_freq ||
				hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)
			if ((hap->act_type == QPNP_HAP_LRA) &&
				(hap->correct_lra_drive_freq ||
				hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD))
				qpnp_hap_auto_res_enable(hap, 0);

			rc = qpnp_hap_mod_enable(hap, on);
@@ -1490,7 +1572,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)

			rc = qpnp_hap_play(hap, on);

			if (hap->act_type == QPNP_HAP_LRA &&
			if ((hap->act_type == QPNP_HAP_LRA) &&
				(hap->correct_lra_drive_freq ||
				hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) {
				usleep_range(back_emf_delay_us,
@@ -1517,7 +1599,8 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
			if (rc < 0)
				return rc;

			if (hap->correct_lra_drive_freq) {
			if (hap->act_type == QPNP_HAP_LRA &&
						hap->correct_lra_drive_freq) {
				rc = qpnp_hap_read_reg(hap, &val,
						QPNP_HAP_STATUS(hap->base));
				if (!(val & AUTO_RES_ERR_BIT))
@@ -1528,7 +1611,6 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
			if (hap->act_type == QPNP_HAP_LRA &&
					hap->correct_lra_drive_freq) {
				hrtimer_cancel(&hap->auto_res_err_poll_timer);
				calculate_lra_code(hap);
			}
		}
	}
@@ -1723,11 +1805,16 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
/* Configuration api for haptics registers */
static int qpnp_hap_config(struct qpnp_hap *hap)
{
	u8 reg = 0, unlock_val, rc_clk_err_percent_x10;
	u8 reg = 0, unlock_val;
	u32 temp;
	int rc, i;
	uint error_code = 0;

	/*
	 * This denotes the percentage error in rc clock multiplied by 10
	 */
	u8 rc_clk_err_percent_x10;

	/* Configure the ACTUATOR TYPE register */
	rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_ACT_TYPE_REG(hap->base));
	if (rc < 0)
@@ -1856,17 +1943,22 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
	else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX)
		hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX;

	temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
	hap->init_drive_period_code =
			 hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;

	/*
	 * The frequency of 19.2Mzhz RC clock is subject to variation. Currently
	 * PMI8950 and PMI8937 modules have MISC_TRIM_ERROR_RC19P2_CLK register
	 * present in the MISC block. This register holds the frequency error
	 * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register
	 * present in their MISC  block. This register holds the frequency error
	 * in 19.2Mhz RC clock.
	 */
	if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
			&& hap->misc_trim_error_rc19p2_clk_reg_present) {
		unlock_val = MISC_SEC_UNLOCK;
		/*
		 * This SID value may change depending on the PMI chip where
		 * the MISC block is present.
		 */
		rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val);
		if (rc)
			dev_err(&hap->pdev->dev,
@@ -1874,7 +1966,12 @@ static int qpnp_hap_config(struct qpnp_hap *hap)

		regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
			    &error_code);
		dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code);

		/*
		 * Extract the 4 LSBs and multiply by 7 to get
		 * the %error in RC clock multiplied by 10
		 */
		rc_clk_err_percent_x10 = (error_code & 0x0F) * 7;

		/*
@@ -1882,40 +1979,56 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
		 * then there is a positive error in the RC clock.
		 * If the TRIM register holds value greater than or equal to
		 * 0x80, then there is a negative error in the RC clock.
		 *
		 * The adjusted play rate code is calculated as follows:
		 * LRA_drive_period_code = (200Khz * (1+%error/100)) / LRA_freq
		 * LRA drive period code (RATE_CFG) =
		 *	 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100)
		 *
		 * This can be rewritten as:
		 * LRA drive period code (RATE_CFG) =
		 *	200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000)
		 *
		 * Since 200KHz * 1/LRA drive frequency is already calculated
		 * above we only do rest of the scaling here.
		 */
		if (error_code >= 128)
			temp = (temp * (1000 - rc_clk_err_percent_x10)) / 1000;
			LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10);
		else
			temp = (temp * (1000 + rc_clk_err_percent_x10)) / 1000;
			LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10);
	}

	dev_dbg(&hap->pdev->dev,
			"TRIM register = 0x%x Play rate code after RC clock correction 0x%x\n",
					error_code, temp);
	}
		 "Play rate code 0x%x\n", hap->init_drive_period_code);

	reg = temp & QPNP_HAP_RATE_CFG1_MASK;
	reg = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK;
	rc = qpnp_hap_write_reg(hap, &reg,
			QPNP_HAP_RATE_CFG1_REG(hap->base));
	if (rc)
		return rc;

	rc = qpnp_hap_read_reg(hap, &reg,
			QPNP_HAP_RATE_CFG2_REG(hap->base));
	if (rc < 0)
		return rc;
	reg &= QPNP_HAP_RATE_CFG2_MASK;
	temp = temp >> QPNP_HAP_RATE_CFG2_SHFT;
	reg |= temp;
	reg = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT;
	rc = qpnp_hap_write_reg(hap, &reg,
			QPNP_HAP_RATE_CFG2_REG(hap->base));
	if (rc)
		return rc;

	if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq)
	if (hap->act_type == QPNP_HAP_LRA &&
				hap->perform_lra_auto_resonance_search)
		calculate_lra_code(hap);

	if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) {
		hap->drive_period_code_max_limit =
			(hap->init_drive_period_code * 100) /
		(100 - hap->drive_period_code_max_limit_percent_variation);
		hap->drive_period_code_min_limit =
			(hap->init_drive_period_code * 100) /
		(100 + hap->drive_period_code_min_limit_percent_variation);
		dev_dbg(&hap->pdev->dev, "Drive period code max limit %x\n"
			"Drive period code min limit %x\n",
				hap->drive_period_code_max_limit,
				hap->drive_period_code_min_limit);
	}

	/* Configure BRAKE register */
	rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_EN_CTL2_REG(hap->base));
	if (rc < 0)
@@ -2062,10 +2175,28 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
			return rc;
		}

		hap->perform_lra_auto_resonance_search =
				of_property_read_bool(pdev->dev.of_node,
				"qcom,perform-lra-auto-resonance-search");

		hap->correct_lra_drive_freq =
				of_property_read_bool(pdev->dev.of_node,
						"qcom,correct-lra-drive-freq");

		hap->drive_period_code_max_limit_percent_variation = 25;
		rc = of_property_read_u32(pdev->dev.of_node,
		"qcom,drive-period-code-max-limit-percent-variation", &temp);
		if (!rc)
			hap->drive_period_code_max_limit_percent_variation =
								(u8) temp;

		hap->drive_period_code_min_limit_percent_variation = 25;
		rc = of_property_read_u32(pdev->dev.of_node,
		"qcom,drive-period-code-min-limit-percent-variation", &temp);
		if (!rc)
			hap->drive_period_code_min_limit_percent_variation =
								(u8) temp;

		hap->misc_trim_error_rc19p2_clk_reg_present =
				of_property_read_bool(pdev->dev.of_node,
				"qcom,misc-trim-error-rc19p2-clk-reg-present");