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

Commit 9f7e29e2 authored by Linux Build Service Account's avatar Linux Build Service Account
Browse files

Promotion of kernel.lnx.4.4-161015.

CRs      Change ID                                   Subject
--------------------------------------------------------------------------------------------------------------
1077022   I0a16e19f825aa192c12c6c7522e417dd99c68f41   ASoC: wcd934x: Update headphone register sequence for wc
1078245   Ib2119bc58a1cdb7a5908db1b8775cb4f13db18f7   scsi: ufs: do clock gating only after hibern8
1077022   Id9fb06fa25dfdd56250a76248f71758af39bade6   drivers: mfd: Add support for wcd934x 1.1 version
1077022   I993273bcf26f8f53f6d9c8cccec5c4b17c8a0e95   ASoC: wcd934x: Update DSD register sequence for v1.1
1075262   I6234e35d76440beb53c139ad0b02ec8b49c2a196   soc: qcom: msm_smem: Pass device structure to ramdump dr
1077022   If2dc9e7fd9a2710391d02c0e52a291b3cffc63b6   ASoC: wcd934x: update keeper enable for soundwire functi
1067588   Icd6846d157af5a96d5ae49261e109cbd5ab8c266   ASoc: wcd9335: Ignore mbhc get impedance error
1074325   If58590fc397ec8eb88906c103247bb48710805db   scsi: ufs: reduce auto hibern8 timeout to save power
1067921   I92b86fad25041311d00fbfc58b3d5620ade7ee38   scsi: ufs: gate ref_clk during aggressive clk gating

Change-Id: Ib63f50604db4f0753f6e9fab6f1c4d61a0e16a1d
CRs-Fixed: 1067921, 1078245, 1077022, 1067588, 1074325, 1075262
parents b54fb1cc 0a97d6bb
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -17,6 +17,18 @@
#include <linux/device.h>
#include "wcd9xxx-regmap.h"


static const struct reg_sequence wcd934x_1_1_defaults[] = {
	{ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,             0x01 },
	{ WCD934X_BIAS_VBG_FINE_ADJ,                        0x75 },
	{ WCD934X_HPH_REFBUFF_LP_CTL,                       0x0E },
	{ WCD934X_EAR_DAC_CTL_ATEST,                        0x08 },
	{ WCD934X_SIDO_NEW_VOUT_A_STARTUP,                  0x17 },
	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,                0x40 },
	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L,               0x81 },
	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R,               0x81 },
};

static const struct reg_default wcd934x_defaults[] = {
	{ WCD934X_PAGE0_PAGE_REGISTER,                      0x00 },
	{ WCD934X_CODEC_RPM_CLK_BYPASS,                     0x00 },
@@ -1803,6 +1815,37 @@ static const struct reg_default wcd934x_defaults[] = {
	{ WCD934X_TEST_DEBUG_CODEC_DIAGS,                  0x00 },
};

/*
 * wcd934x_regmap_register_patch: Update register defaults based on version
 * @regmap: handle to wcd9xxx regmap
 * @version: wcd934x version
 *
 * Returns error code in case of failure or 0 for success
 */
int wcd934x_regmap_register_patch(struct regmap *regmap, int revision)
{
	int rc = 0;

	if (!regmap) {
		pr_err("%s: regmap struct is NULL\n", __func__);
		return -EINVAL;
	}

	switch (revision) {
	case TAVIL_VERSION_1_1:
	case TAVIL_VERSION_WCD9340_1_1:
	case TAVIL_VERSION_WCD9341_1_1:
		regcache_cache_only(regmap, true);
		rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults,
					    ARRAY_SIZE(wcd934x_1_1_defaults));
		regcache_cache_only(regmap, false);
		break;
	}

	return rc;
}
EXPORT_SYMBOL(wcd934x_regmap_register_patch);

static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg)
{
	u8 pg_num, reg_offset;
+7 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ typedef int (*regmap_patch_fptr)(struct regmap *, int);

#ifdef CONFIG_WCD934X_CODEC
extern struct regmap_config wcd934x_regmap_config;
extern int wcd934x_regmap_register_patch(struct regmap *regmap,
					 int version);
#endif

#ifdef CONFIG_WCD9335_CODEC
@@ -70,6 +72,11 @@ static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type)
	case WCD9335:
		apply_patch = wcd9335_regmap_register_patch;
		break;
#endif
#ifdef CONFIG_WCD934X_CODEC
	case WCD934X:
		apply_patch = wcd934x_regmap_register_patch;
		break;
#endif
	default:
		apply_patch = NULL;
+8 −11
Original line number Diff line number Diff line
@@ -1498,8 +1498,12 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,

		/* M-PHY RMMI interface clocks can be turned off */
		ufs_qcom_phy_disable_iface_clk(host->generic_phy);
		if (!ufs_qcom_is_link_active(hba)) {
			if (!is_gating_context)
		/*
		 * If auto hibern8 is supported then the link will already
		 * be in hibern8 state and the ref clock can be gated.
		 */
		if (ufshcd_is_auto_hibern8_supported(hba) ||
		    !ufs_qcom_is_link_active(hba)) {
			/* turn off UFS local PHY ref_clk */
			ufs_qcom_phy_disable_ref_clk(host->generic_phy);
			/* disable device ref_clk */
@@ -1956,13 +1960,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
	host->hba = hba;
	ufshcd_set_variant(hba, host);

	/*
	 * voting/devoting device ref_clk source is time consuming hence
	 * skip devoting it during aggressive clock gating. This clock
	 * will still be gated off during runtime suspend.
	 */
	hba->no_ref_clk_gating = true;

	err = ufs_qcom_ice_get_dev(host);
	if (err == -EPROBE_DEFER) {
		/*
+52 −26
Original line number Diff line number Diff line
@@ -1176,6 +1176,12 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
	return ret;
}

static inline void ufshcd_cancel_gate_work(struct ufs_hba *hba)
{
	hrtimer_cancel(&hba->clk_gating.gate_hrtimer);
	cancel_work_sync(&hba->clk_gating.gate_work);
}

static void ufshcd_ungate_work(struct work_struct *work)
{
	int ret;
@@ -1183,7 +1189,7 @@ static void ufshcd_ungate_work(struct work_struct *work)
	struct ufs_hba *hba = container_of(work, struct ufs_hba,
			clk_gating.ungate_work);

	cancel_delayed_work_sync(&hba->clk_gating.gate_work);
	ufshcd_cancel_gate_work(hba);

	spin_lock_irqsave(hba->host->host_lock, flags);
	if (hba->clk_gating.state == CLKS_ON) {
@@ -1254,14 +1260,18 @@ start:
		}
		break;
	case REQ_CLKS_OFF:
		if (cancel_delayed_work(&hba->clk_gating.gate_work)) {
		/*
		 * If the timer was active but the callback was not running
		 * we have nothing to do, just change state and return.
		 */
		if (hrtimer_try_to_cancel(&hba->clk_gating.gate_hrtimer) == 1) {
			hba->clk_gating.state = CLKS_ON;
			trace_ufshcd_clk_gating(dev_name(hba->dev),
				hba->clk_gating.state);
			break;
		}
		/*
		 * If we here, it means gating work is either done or
		 * If we are here, it means gating work is either done or
		 * currently running. Hence, fall through to cancel gating
		 * work and to enable clocks.
		 */
@@ -1301,7 +1311,7 @@ EXPORT_SYMBOL_GPL(ufshcd_hold);
static void ufshcd_gate_work(struct work_struct *work)
{
	struct ufs_hba *hba = container_of(work, struct ufs_hba,
			clk_gating.gate_work.work);
						clk_gating.gate_work);
	unsigned long flags;

	spin_lock_irqsave(hba->host->host_lock, flags);
@@ -1346,7 +1356,12 @@ static void ufshcd_gate_work(struct work_struct *work)
		ufshcd_set_link_hibern8(hba);
	}

	if (!ufshcd_is_link_active(hba) && !hba->no_ref_clk_gating)
	/*
	 * If auto hibern8 is supported then the link will already
	 * be in hibern8 state and the ref clock can be gated.
	 */
	if ((ufshcd_is_auto_hibern8_supported(hba) ||
	     !ufshcd_is_link_active(hba)) && !hba->no_ref_clk_gating)
		ufshcd_disable_clocks(hba, true);
	else
		/* If link is active, device ref_clk can't be switched off */
@@ -1394,8 +1409,9 @@ static void __ufshcd_release(struct ufs_hba *hba, bool no_sched)
	hba->clk_gating.state = REQ_CLKS_OFF;
	trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state);

	schedule_delayed_work(&hba->clk_gating.gate_work,
			      msecs_to_jiffies(hba->clk_gating.delay_ms));
	hrtimer_start(&hba->clk_gating.gate_hrtimer,
			ms_to_ktime(hba->clk_gating.delay_ms),
			HRTIMER_MODE_REL);
}

void ufshcd_release(struct ufs_hba *hba, bool no_sched)
@@ -1523,6 +1539,17 @@ out:
	return count;
}

static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler(
					struct hrtimer *timer)
{
	struct ufs_hba *hba = container_of(timer, struct ufs_hba,
					   clk_gating.gate_hrtimer);

	schedule_work(&hba->clk_gating.gate_work);

	return HRTIMER_NORESTART;
}

static void ufshcd_init_clk_gating(struct ufs_hba *hba)
{
	struct ufs_clk_gating *gating = &hba->clk_gating;
@@ -1539,27 +1566,25 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba)
	if (ufshcd_is_auto_hibern8_supported(hba))
		hba->caps &= ~UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;

	INIT_DELAYED_WORK(&gating->gate_work, ufshcd_gate_work);
	INIT_WORK(&gating->gate_work, ufshcd_gate_work);
	INIT_WORK(&gating->ungate_work, ufshcd_ungate_work);
	/*
	 * Clock gating work must be executed only after auto hibern8
	 * timeout has expired in the hardware or after aggressive
	 * hibern8 on idle software timeout. Using jiffy based low
	 * resolution delayed work is not reliable to guarantee this,
	 * hence use a high resolution timer to make sure we schedule
	 * the gate work precisely more than hibern8 timeout.
	 *
	 * Always make sure gating->delay_ms > hibern8_on_idle->delay_ms
	 */
	hrtimer_init(&gating->gate_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	gating->gate_hrtimer.function = ufshcd_clkgate_hrtimer_handler;

	gating->is_enabled = true;

	/*
	 * Scheduling the delayed work after 1 jiffies will make the work to
	 * get schedule any time from 0ms to 1000/HZ ms which is not desirable
	 * for hibern8 enter work as it may impact the performance if it gets
	 * scheduled almost immediately. Hence make sure that hibern8 enter
	 * work gets scheduled atleast after 2 jiffies (any time between
	 * 1000/HZ ms to 2000/HZ ms).
	 */
	gating->delay_ms_pwr_save = jiffies_to_msecs(
		max_t(unsigned long,
		      msecs_to_jiffies(UFSHCD_CLK_GATING_DELAY_MS_PWR_SAVE),
		      2));
	gating->delay_ms_perf = jiffies_to_msecs(
		max_t(unsigned long,
		      msecs_to_jiffies(UFSHCD_CLK_GATING_DELAY_MS_PERF),
		      2));
	gating->delay_ms_pwr_save = UFSHCD_CLK_GATING_DELAY_MS_PWR_SAVE;
	gating->delay_ms_perf = UFSHCD_CLK_GATING_DELAY_MS_PERF;

	/* start with performance mode */
	gating->delay_ms = gating->delay_ms_perf;
@@ -1616,8 +1641,8 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
		device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
	}
	device_remove_file(hba->dev, &hba->clk_gating.enable_attr);
	ufshcd_cancel_gate_work(hba);
	cancel_work_sync(&hba->clk_gating.ungate_work);
	cancel_delayed_work_sync(&hba->clk_gating.gate_work);
}

static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay)
@@ -1928,6 +1953,7 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
		return;

	if (ufshcd_is_auto_hibern8_supported(hba)) {
		hba->hibern8_on_idle.delay_ms = 1;
		hba->hibern8_on_idle.state = AUTO_HIBERN8;
		/*
		 * Disable SW hibern8 enter on idle in case
@@ -1935,13 +1961,13 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
		 */
		hba->caps &= ~UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE;
	} else {
		hba->hibern8_on_idle.delay_ms = 10;
		INIT_DELAYED_WORK(&hba->hibern8_on_idle.enter_work,
				  ufshcd_hibern8_enter_work);
		INIT_WORK(&hba->hibern8_on_idle.exit_work,
			  ufshcd_hibern8_exit_work);
	}

	hba->hibern8_on_idle.delay_ms = 10;
	hba->hibern8_on_idle.is_enabled = true;

	hba->hibern8_on_idle.delay_attr.show =
+6 −3
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -396,8 +397,9 @@ enum clk_gating_state {

/**
 * struct ufs_clk_gating - UFS clock gating related info
 * @gate_work: worker to turn off clocks after some delay as specified in
 * delay_ms
 * @gate_hrtimer: hrtimer to invoke @gate_work after some delay as
 * specified in @delay_ms
 * @gate_work: worker to turn off clocks
 * @ungate_work: worker to turn on clocks that will be used in case of
 * interrupt context
 * @state: the current clocks state
@@ -415,7 +417,8 @@ enum clk_gating_state {
 * completion before gating clocks.
 */
struct ufs_clk_gating {
	struct delayed_work gate_work;
	struct hrtimer gate_hrtimer;
	struct work_struct gate_work;
	struct work_struct ungate_work;
	enum clk_gating_state state;
	unsigned long delay_ms;
Loading