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

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

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

parents 576cf925 11a53aed
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;
};