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

Commit 11a53aed authored by Ankit Gupta's avatar Ankit Gupta
Browse files

i2c-msm-v2: correct and improve clock control calculation



On some targets t-low is below i2c spec for 400KHz. Also,
clock control value is calulated per transfer, when calculting
it once is sufficient. This patch warns when device tree configures
an unsuppurted bus frequency and removed related unused debugfs
entries.

Change-Id: I5f721426c7deeac31ee20512816759f1c6407f80
Signed-off-by: default avatarSana Venkat Raju <c_vsana@codeaurora.org>
Signed-off-by: default avatarAnkit Gupta <ankgupta@codeaurora.org>
parent 408d933c
Loading
Loading
Loading
Loading
+61 −75
Original line number Diff line number Diff line
@@ -1073,26 +1073,65 @@ bool i2c_msm_xfer_is_high_speed(struct i2c_msm_ctrl *ctrl)
}

/*
 * i2c_msm_qup_xfer_init_run_state: set qup regs which must be set *after* reset
 * i2c_msm_clk_div_fld:
 * @clk_freq_out output clock frequency
 * @fs_div fs divider value
 * @ht_div high time divider value
 */
static void i2c_msm_qup_xfer_init_run_state(struct i2c_msm_ctrl *ctrl)
struct i2c_msm_clk_div_fld {
	u32                clk_freq_out;
	u8                 fs_div;
	u8                 ht_div;
};

/*
 * divider values as per HW Designers
 */
static struct i2c_msm_clk_div_fld i2c_msm_clk_div_map[] = {
	{KHz(100), 124, 62},
	{KHz(400),  28, 14},
	{KHz(1000),  8,  5},
};

/* @return zero on success */
static int i2c_msm_set_mstr_clk_ctl(struct i2c_msm_ctrl *ctrl)
{
	void __iomem *base = ctrl->rsrcs.base;
	u32 val = 0;
	int fs_div = 0;
	int ht_div = 0;
	bool match = false;
	int i;
	u32 reg_val = 0;
	struct i2c_msm_clk_div_fld *itr = i2c_msm_clk_div_map;

	/* set noise rejection values for scl and sda */
	reg_val = I2C_MSM_SCL_NOISE_REJECTION(reg_val, ctrl->noise_rjct_scl);
	reg_val = I2C_MSM_SDA_NOISE_REJECTION(reg_val, ctrl->noise_rjct_sda);

	/* set divider values */
	for (i = 0; i < ARRAY_SIZE(i2c_msm_clk_div_map); ++i, ++itr) {
		if (ctrl->rsrcs.clk_freq_out == itr->clk_freq_out) {
			fs_div = itr->fs_div;
			ht_div = itr->ht_div;
			match  = true;
			break;
		}
	}
	ctrl->mstr_clk_ctl = (reg_val & (~0xff07ff)) | ((ht_div & 0xff) << 16)
				|(fs_div & 0xff);

	if (i2c_msm_xfer_is_high_speed(ctrl)) {
		val = I2C_MSM_SCL_NOISE_REJECTION(val, ctrl->noise_rjct_scl);
		val = I2C_MSM_SDA_NOISE_REJECTION(val, ctrl->noise_rjct_sda);
		val = I2C_MSM_CLK_DIV(val, ctrl->rsrcs.clk_freq_in,
					ctrl->rsrcs.clk_freq_out, true);
	} else {
		val = I2C_MSM_SCL_NOISE_REJECTION(val, ctrl->noise_rjct_scl);
		val = I2C_MSM_SDA_NOISE_REJECTION(val, ctrl->noise_rjct_sda);
		val = I2C_MSM_CLK_DIV(val, ctrl->rsrcs.clk_freq_in,
					ctrl->rsrcs.clk_freq_out, false);
	if (!match)
		dev_err(ctrl->dev, "error clock frequency %dKHz is not supported\n"
					, (ctrl->rsrcs.clk_freq_out / 1000));
	return !match;
}

	writel_relaxed(val, base + QUP_I2C_MASTER_CLK_CTL);
/*
 * i2c_msm_qup_xfer_init_run_state: set qup regs which must be set *after* reset
 */
static void i2c_msm_qup_xfer_init_run_state(struct i2c_msm_ctrl *ctrl)
{
	void __iomem *base = ctrl->rsrcs.base;
	writel_relaxed(ctrl->mstr_clk_ctl, base + QUP_I2C_MASTER_CLK_CTL);

	/* Ensure that QUP configuration is written before leaving this func */
	wmb();
@@ -2156,6 +2195,7 @@ static void i2c_msm_bam_teardown(struct i2c_msm_ctrl *ctrl)
	dma_free_coherent(ctrl->dev, I2C_MSM_BAM_TAG_MEM_SZ,
			tags_space_virt_addr, tags_space_phy_addr);

	iounmap(bam->base);
	bam->is_init      = false;
	bam->is_core_init = false;
}
@@ -3644,60 +3684,6 @@ static void i2c_msm_rsrcs_clk_teardown(struct i2c_msm_ctrl *ctrl)
}

#ifdef CONFIG_DEBUG_FS
static int i2c_msm_dbgfs_noise_scl_read(void *data, u64 *val)
{
	struct i2c_msm_ctrl *ctrl = data;
	*val = ctrl->noise_rjct_scl;
	return 0;
}

static int i2c_msm_dbgfs_noise_scl_write(void *data, u64 val)
{
	struct i2c_msm_ctrl *ctrl = data;

	if (val < 0 || val > 3) {
		dev_err(ctrl->dev,
			"error dbgfs attempt to set invalid value for noise"
			" reject. Should be 0..3\n");
		return -EINVAL;
	}

	ctrl->noise_rjct_scl = val;
	return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(i2c_msm_dbgfs_noise_scl_fops,
			i2c_msm_dbgfs_noise_scl_read,
			i2c_msm_dbgfs_noise_scl_write,
			"0x%llx");

static int i2c_msm_dbgfs_noise_sda_read(void *data, u64 *val)
{
	struct i2c_msm_ctrl *ctrl = data;
	*val = ctrl->noise_rjct_sda;
	return 0;
}

static int i2c_msm_dbgfs_noise_sda_write(void *data, u64 val)
{
	struct i2c_msm_ctrl *ctrl = data;

	if (val < 0 || val > 3) {
		dev_err(ctrl->dev,
			"error dbgfs attempt to set invalid value for noise"
			" reject. Should be 0..3\n");
		return -EINVAL;
	}

	ctrl->noise_rjct_sda = val;
	return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(i2c_msm_dbgfs_noise_sda_fops,
			i2c_msm_dbgfs_noise_sda_read,
			i2c_msm_dbgfs_noise_sda_write,
			"0x%llx");

/*
 * i2c_msm_dbgfs_clk_wrapper: take care of clocks before calling func
 *
@@ -3828,16 +3814,10 @@ static void i2c_msm_dbgfs_init(struct i2c_msm_ctrl *ctrl)
				NULL, &ctrl->dbgfs.dbg_lvl},
		{"xfer-force-mode", I2C_MSM_DFS_MD_RW, I2C_MSM_DFS_U8,
				NULL, &ctrl->dbgfs.force_xfer_mode},
		{"noise-rjct-scl",  I2C_MSM_DFS_MD_RW, I2C_MSM_DFS_FILE,
				&i2c_msm_dbgfs_noise_scl_fops,     NULL},
		{"noise-rjct-sda",  I2C_MSM_DFS_MD_RW, I2C_MSM_DFS_FILE,
				&i2c_msm_dbgfs_noise_sda_fops,     NULL},
		{"dump-regs",       I2C_MSM_DFS_MD_W, I2C_MSM_DFS_FILE,
				&i2c_msm_dbgfs_reg_dump_fops,      NULL},
		{"bus-clear",       I2C_MSM_DFS_MD_W, I2C_MSM_DFS_FILE,
				&i2c_msm_dbgfs_do_bus_clear_fops,  NULL},
		{"freq-out-hz",     I2C_MSM_DFS_MD_RW, I2C_MSM_DFS_U32,
				NULL, &ctrl->rsrcs.clk_freq_out},
		{NULL, 0, 0, NULL , NULL}, /* null terminator */
	};
	return i2c_msm_dbgfs_create(ctrl, i2c_msm_dbgfs_map);
@@ -4061,6 +4041,12 @@ static int i2c_msm_probe(struct platform_device *pdev)
		dev_err(ctrl->dev, "error in enabling clocks:%d\n", ret);
		goto clk_err;
	}

	/* set divider and noise reject values */
	ret = i2c_msm_set_mstr_clk_ctl(ctrl);
	if (ret)
		goto clk_err;

	ret = i2c_msm_ctrl_ver_detect_and_set(ctrl);
	if (ret) {
		i2c_msm_pm_clk_disable_unprepare(ctrl);
+3 −19
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ enum msm_i2_debug_level {
#define BITS_AT(val, idx, n_bits)(((val) & (((1 << n_bits) - 1) << idx)) >> idx)
#define MASK_IS_SET(val, mask)      ((val & mask) == mask)
#define MASK_IS_SET_BOOL(val, mask) (MASK_IS_SET(val, mask) ? 1 : 0)
#define KHz(freq) (1000 * freq)

/* QUP Registers */
enum {
@@ -142,7 +143,6 @@ enum {
};

enum {
	I2C_MSM_CLK_FAST_FREQ_HS     =  400000,
	I2C_MSM_CLK_FAST_MAX_FREQ    = 1000000,
	I2C_MSM_CLK_HIGH_MAX_FREQ    = 3400000,
};
@@ -152,24 +152,6 @@ enum {
		(((reg_val) & ~(0x3 << 24)) | (((noise_rej_val) & 0x3) << 24))
#define I2C_MSM_SDA_NOISE_REJECTION(reg_val, noise_rej_val) \
		(((reg_val) & ~(0x3 << 26)) | (((noise_rej_val) & 0x3) << 26))
static inline u32 I2C_MSM_CLK_DIV(u32 reg_val, u32 clk_freq_in,
				u32 clk_freq_out, bool is_high_speed)
{
	int fs_div;
	int hs_div;

	if (is_high_speed) {
		fs_div = I2C_MSM_CLK_FAST_FREQ_HS;
		hs_div = (clk_freq_in / (clk_freq_out * 3));
	} else {
		fs_div = (clk_freq_in / (clk_freq_out * 2)) - 3;
		hs_div = 0;
	}
	/* Protect hs_div from overflow (it is represented in HW by 3 bits */
	hs_div = min_t(int, hs_div, 0x7);

	return (reg_val & (~0x7ff)) | ((hs_div & 0x7) << 8) | (fs_div & 0xff);
}

/* Register:QUP_ERROR_FLAGS_EN flags */
enum {
@@ -674,6 +656,7 @@ struct i2c_msm_xfer {
 * @noise_rjct_sda noise rejection value for the sda line (a field of
 *           I2C_MASTER_CLK_CTL).
 * @pdata    the platform data (values from board-file or from device-tree)
 * @mstr_clk_ctl cached value for programming to mstr_clk_ctl register
 */
struct i2c_msm_ctrl {
	struct device             *dev;
@@ -684,6 +667,7 @@ struct i2c_msm_ctrl {
	struct i2c_msm_resources   rsrcs;
	int                        noise_rjct_scl;
	int                        noise_rjct_sda;
	u32                        mstr_clk_ctl;
	struct i2c_msm_v2_platform_data *pdata;
	enum msm_i2c_power_state   pwr_state;
};