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: Jun Gao <jun.gao@mediatek.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
16f73eb02d
commit
f2326401b8
@ -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 {
|
|||||||
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)
|
|||||||
* 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,
|
|||||||
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 = {
|
|||||||
.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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user