Commit f2326401 authored by Jun Gao's avatar Jun Gao Committed by Wolfram Sang

i2c: mediatek: send i2c master code at 400k

The speed of sending i2c master code in high-speed mode depends on
source clock, clock-div and TIMING register. The source clock and
clock-div of different SoC are not all the same. In order to send
i2c master code at 400k in high-speed mode, a appropriate value
should be set to TIMING register for a certain source clock and
clock-div.
Signed-off-by: default avatarJun Gao <jun.gao@mediatek.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 16f73eb0
...@@ -50,7 +50,6 @@ ...@@ -50,7 +50,6 @@
#define I2C_FS_START_CON 0x1800 #define I2C_FS_START_CON 0x1800
#define I2C_TIME_CLR_VALUE 0x0000 #define I2C_TIME_CLR_VALUE 0x0000
#define I2C_TIME_DEFAULT_VALUE 0x0003 #define I2C_TIME_DEFAULT_VALUE 0x0003
#define I2C_FS_TIME_INIT_VALUE 0x1303
#define I2C_WRRD_TRANAC_VALUE 0x0002 #define I2C_WRRD_TRANAC_VALUE 0x0002
#define I2C_RD_TRANAC_VALUE 0x0001 #define I2C_RD_TRANAC_VALUE 0x0001
...@@ -154,6 +153,7 @@ struct mtk_i2c { ...@@ -154,6 +153,7 @@ struct mtk_i2c {
bool use_push_pull; /* IO config push-pull mode */ bool use_push_pull; /* IO config push-pull mode */
u16 irq_stat; /* interrupt status */ u16 irq_stat; /* interrupt status */
unsigned int clk_src_div;
unsigned int speed_hz; /* The speed in transfer */ unsigned int speed_hz; /* The speed in transfer */
enum mtk_trans_op op; enum mtk_trans_op op;
u16 timing_reg; u16 timing_reg;
...@@ -285,23 +285,20 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c) ...@@ -285,23 +285,20 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
* less than or equal to i2c->speed_hz. The calculation try to get * less than or equal to i2c->speed_hz. The calculation try to get
* sample_cnt and step_cn * sample_cnt and step_cn
*/ */
static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
unsigned int clock_div) unsigned int target_speed,
unsigned int *timing_step_cnt,
unsigned int *timing_sample_cnt)
{ {
unsigned int clk_src;
unsigned int step_cnt; unsigned int step_cnt;
unsigned int sample_cnt; unsigned int sample_cnt;
unsigned int max_step_cnt; unsigned int max_step_cnt;
unsigned int target_speed;
unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV; unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV;
unsigned int base_step_cnt; unsigned int base_step_cnt;
unsigned int opt_div; unsigned int opt_div;
unsigned int best_mul; unsigned int best_mul;
unsigned int cnt_mul; unsigned int cnt_mul;
clk_src = parent_clk / clock_div;
target_speed = i2c->speed_hz;
if (target_speed > MAX_HS_MODE_SPEED) if (target_speed > MAX_HS_MODE_SPEED)
target_speed = MAX_HS_MODE_SPEED; target_speed = MAX_HS_MODE_SPEED;
...@@ -347,16 +344,48 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, ...@@ -347,16 +344,48 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk,
return -EINVAL; return -EINVAL;
} }
step_cnt--; *timing_step_cnt = step_cnt - 1;
sample_cnt--; *timing_sample_cnt = sample_cnt - 1;
return 0;
}
static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
{
unsigned int clk_src;
unsigned int step_cnt;
unsigned int sample_cnt;
unsigned int target_speed;
int ret;
clk_src = parent_clk / i2c->clk_src_div;
target_speed = i2c->speed_hz;
if (target_speed > MAX_FS_MODE_SPEED) { if (target_speed > MAX_FS_MODE_SPEED) {
/* Set master code speed register */
ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED,
&step_cnt, &sample_cnt);
if (ret < 0)
return ret;
i2c->timing_reg = (sample_cnt << 8) | step_cnt;
/* Set the high speed mode register */ /* Set the high speed mode register */
i2c->timing_reg = I2C_FS_TIME_INIT_VALUE; ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
&step_cnt, &sample_cnt);
if (ret < 0)
return ret;
i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE | i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
(sample_cnt << 12) | (step_cnt << 8); (sample_cnt << 12) | (step_cnt << 8);
} else { } else {
i2c->timing_reg = (sample_cnt << 8) | (step_cnt << 0); ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
&step_cnt, &sample_cnt);
if (ret < 0)
return ret;
i2c->timing_reg = (sample_cnt << 8) | step_cnt;
/* Disable the high speed transaction */ /* Disable the high speed transaction */
i2c->high_speed_reg = I2C_TIME_CLR_VALUE; i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
} }
...@@ -647,8 +676,7 @@ static const struct i2c_algorithm mtk_i2c_algorithm = { ...@@ -647,8 +676,7 @@ static const struct i2c_algorithm mtk_i2c_algorithm = {
.functionality = mtk_i2c_functionality, .functionality = mtk_i2c_functionality,
}; };
static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c)
unsigned int *clk_src_div)
{ {
int ret; int ret;
...@@ -656,11 +684,11 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, ...@@ -656,11 +684,11 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c,
if (ret < 0) if (ret < 0)
i2c->speed_hz = I2C_DEFAULT_SPEED; i2c->speed_hz = I2C_DEFAULT_SPEED;
ret = of_property_read_u32(np, "clock-div", clk_src_div); ret = of_property_read_u32(np, "clock-div", &i2c->clk_src_div);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (*clk_src_div == 0) if (i2c->clk_src_div == 0)
return -EINVAL; return -EINVAL;
i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic"); i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic");
...@@ -676,7 +704,6 @@ static int mtk_i2c_probe(struct platform_device *pdev) ...@@ -676,7 +704,6 @@ static int mtk_i2c_probe(struct platform_device *pdev)
int ret = 0; int ret = 0;
struct mtk_i2c *i2c; struct mtk_i2c *i2c;
struct clk *clk; struct clk *clk;
unsigned int clk_src_div;
struct resource *res; struct resource *res;
int irq; int irq;
...@@ -684,7 +711,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) ...@@ -684,7 +711,7 @@ static int mtk_i2c_probe(struct platform_device *pdev)
if (!i2c) if (!i2c)
return -ENOMEM; return -ENOMEM;
ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c, &clk_src_div); ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
if (ret) if (ret)
return -EINVAL; return -EINVAL;
...@@ -745,7 +772,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) ...@@ -745,7 +772,7 @@ static int mtk_i2c_probe(struct platform_device *pdev)
strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk), clk_src_div); ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to set the speed.\n"); dev_err(&pdev->dev, "Failed to set the speed.\n");
return -EINVAL; return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment