RE: [PATCH] i2c: imx: update i2c clock divider for each transaction

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Fugang Duan [mailto:b38611@xxxxxxxxxxxxx]
> Sent: Monday, May 19, 2014 9:22 PM
> To: wsa@xxxxxxxxxxxxx
> Cc: linux-i2c@xxxxxxxxxxxxxxx; u.kleine-koenig@xxxxxxxxxxxxxx; Duan
> Fugang-B38611; Li Frank-B20596
> Subject: [PATCH] i2c: imx: update i2c clock divider for each transaction
> 
> Since IMX serial SOCs support low bus freq mode, some clocks freq may
> change to save power. I2C needs to check the clock source and update the
> divider.
> 
> For example:
> i.MX6SL I2C clk is from IPG_PERCLK which is sourced from IPG_CLK.
> Under normal operation, IPG_CLK is 66MHz, ipg_perclk is at 22MHz.
> In low bus freq mode, IPG_CLK is at 12MHz and IPG_PERCLK is down to 4MHz.
> So the I2C driver must update the divider register for each transaction
> when the current IPG_PERCLK is not equal to the clock of previous
> transaction.
> 
> Signed-off-by: Fugang Duan  <B38611@xxxxxxxxxxxxx>

Acked by: Frank Li<frank.li@xxxxxxxxxxxxx>

> ---
>  drivers/i2c/busses/i2c-imx.c |   94 ++++++++++++++++++++++---------------
> ----
>  1 files changed, 50 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> index 6c2ac05..2fdff4d 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c
> @@ -183,6 +183,8 @@ struct imx_i2c_struct {
>  	unsigned int 		disable_delay;
>  	int			stopped;
>  	unsigned int		ifdr; /* IMX_I2C_IFDR */
> +	unsigned int		cur_clk;
> +	unsigned int		bitrate;
>  	const struct imx_i2c_hwdata	*hwdata;
>  };
> 
> @@ -305,6 +307,49 @@ static int i2c_imx_acked(struct imx_i2c_struct
> *i2c_imx)
>  	return 0;
>  }
> 
> +static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx) {
> +	struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
> +	unsigned int i2c_clk_rate;
> +	unsigned int div;
> +	int i;
> +
> +	/* Divider value calculation */
> +	i2c_clk_rate = clk_get_rate(i2c_imx->clk);
> +	if (i2c_imx->cur_clk == i2c_clk_rate)
> +		return;
> +	else
> +		i2c_imx->cur_clk = i2c_clk_rate;
> +
> +	div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate;
> +	if (div < i2c_clk_div[0].div)
> +		i = 0;
> +	else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
> +		i = i2c_imx->hwdata->ndivs - 1;
> +	else
> +		for (i = 0; i2c_clk_div[i].div < div; i++);
> +
> +	/* Store divider value */
> +	i2c_imx->ifdr = i2c_clk_div[i].val;
> +
> +	/*
> +	 * There dummy delay is calculated.
> +	 * It should be about one I2C clock period long.
> +	 * This delay is used in I2C bus disable function
> +	 * to fix chip hardware bug.
> +	 */
> +	i2c_imx->disable_delay = (500000U * i2c_clk_div[i].div
> +		+ (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2);
> +
> +	/* dev_dbg() can't be used, because adapter is not yet registered */
> +#ifdef CONFIG_I2C_DEBUG_BUS
> +	dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
> +		__func__, i2c_clk_rate, div);
> +	dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
> +		__func__, i2c_clk_div[i].val, i2c_clk_div[i].div); #endif }
> +
>  static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)  {
>  	unsigned int temp = 0;
> @@ -312,6 +357,8 @@ static int i2c_imx_start(struct imx_i2c_struct
> *i2c_imx)
> 
>  	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> 
> +	i2c_imx_set_clk(i2c_imx);
> +
>  	result = clk_prepare_enable(i2c_imx->clk);
>  	if (result)
>  		return result;
> @@ -367,45 +414,6 @@ static void i2c_imx_stop(struct imx_i2c_struct
> *i2c_imx)
>  	clk_disable_unprepare(i2c_imx->clk);
>  }
> 
> -static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
> -							unsigned int rate)
> -{
> -	struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
> -	unsigned int i2c_clk_rate;
> -	unsigned int div;
> -	int i;
> -
> -	/* Divider value calculation */
> -	i2c_clk_rate = clk_get_rate(i2c_imx->clk);
> -	div = (i2c_clk_rate + rate - 1) / rate;
> -	if (div < i2c_clk_div[0].div)
> -		i = 0;
> -	else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
> -		i = i2c_imx->hwdata->ndivs - 1;
> -	else
> -		for (i = 0; i2c_clk_div[i].div < div; i++);
> -
> -	/* Store divider value */
> -	i2c_imx->ifdr = i2c_clk_div[i].val;
> -
> -	/*
> -	 * There dummy delay is calculated.
> -	 * It should be about one I2C clock period long.
> -	 * This delay is used in I2C bus disable function
> -	 * to fix chip hardware bug.
> -	 */
> -	i2c_imx->disable_delay = (500000U * i2c_clk_div[i].div
> -		+ (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2);
> -
> -	/* dev_dbg() can't be used, because adapter is not yet registered */
> -#ifdef CONFIG_I2C_DEBUG_BUS
> -	dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
> -		__func__, i2c_clk_rate, div);
> -	dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
> -		__func__, i2c_clk_div[i].val, i2c_clk_div[i].div);
> -#endif
> -}
> -
>  static irqreturn_t i2c_imx_isr(int irq, void *dev_id)  {
>  	struct imx_i2c_struct *i2c_imx = dev_id; @@ -619,7 +627,6 @@ static
> int i2c_imx_probe(struct platform_device *pdev)
>  	struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
>  	void __iomem *base;
>  	int irq, ret;
> -	u32 bitrate;
> 
>  	dev_dbg(&pdev->dev, "<%s>\n", __func__);
> 
> @@ -683,12 +690,11 @@ static int i2c_imx_probe(struct platform_device
> *pdev)
>  	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
> 
>  	/* Set up clock divider */
> -	bitrate = IMX_I2C_BIT_RATE;
> +	i2c_imx->bitrate = IMX_I2C_BIT_RATE;
>  	ret = of_property_read_u32(pdev->dev.of_node,
> -				   "clock-frequency", &bitrate);
> +				   "clock-frequency", &i2c_imx->bitrate);
>  	if (ret < 0 && pdata && pdata->bitrate)
> -		bitrate = pdata->bitrate;
> -	i2c_imx_set_clk(i2c_imx, bitrate);
> +		i2c_imx->bitrate = pdata->bitrate;
> 
>  	/* Set up chip registers to defaults */
>  	imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
> --
> 1.7.8

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux