[PATCH linux dev-5.1 v1] ARM: ASPEED: I2C: Update I2C driver to use APB_clk

Hongwei Zhang hongweiz at ami.com
Sat Jun 8 06:39:41 AEST 2019


Update I2C clock driver to calculate the clock frequency based on
apb_clk, instead of bus's parent clock frequency.

Signed-off-by: Hongwei Zhang <hongweiz at ami.com>
---
 drivers/i2c/busses/i2c-aspeed.c | 45 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 6c8b38f..9cce1fe 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -167,6 +167,7 @@ struct aspeed_i2c_bus {
 };
 
 static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus);
+static u32 calculate_APB_clock_freq(struct platform_device *pdev);
 
 static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
 {
@@ -862,11 +863,11 @@ static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor)
 }
 
 /* precondition: bus.lock has been acquired. */
-static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus)
+static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus, u32 apb_clk)
 {
 	u32 divisor, clk_reg_val;
 
-	divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency);
+	divisor = DIV_ROUND_UP(apb_clk, bus->bus_frequency);
 	clk_reg_val = readl(bus->base + ASPEED_I2C_AC_TIMING_REG1);
 	clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK |
 			ASPEED_I2CD_TIME_THDSTA_MASK |
@@ -883,12 +884,16 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
 			     struct platform_device *pdev)
 {
 	u32 fun_ctrl_reg = ASPEED_I2CD_MASTER_EN;
+	u32 apb_clk;
 	int ret;
 
 	/* Disable everything. */
 	writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG);
 
-	ret = aspeed_i2c_init_clk(bus);
+	apb_clk = calculate_APB_clock_freq(pdev);
+
+	ret = aspeed_i2c_init_clk(bus, apb_clk);
+
 	if (ret < 0)
 		return ret;
 
@@ -945,6 +950,40 @@ static const struct of_device_id aspeed_i2c_bus_of_table[] = {
 };
 MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
 
+static u32 calculate_APB_clock_freq(struct platform_device *pdev)
+{
+	u8 pclk_div;
+	u32 scu_csr, scu_hpll;
+	u32 apb_clk, output_freq;
+	char *baseptr = ioremap(0x1e6e2000, 0x28);
+	const struct of_device_id *of_id = of_match_device(aspeed_i2c_bus_of_table, &pdev->dev);
+
+	scu_csr = readl(baseptr + 0x08);
+	scu_hpll = readl(baseptr + 0x24);
+	pclk_div = (scu_csr & 0x03800000) >> 23; /* 23:25 */
+
+	if (strcmp(of_id->compatible, "aspeed, ast2400-i2c-bus") == 0) {
+		u8 OD, D, N;
+
+		D = scu_hpll & 0x0000000F;         /* 0:3  */
+		OD = (scu_hpll & 0x00000010) >> 4; /* 4    */
+		N = (scu_hpll & 0x000007E0) >> 5;  /* 5:10 */
+		output_freq = (24 * (2 - OD) * ((N+2) / (D+1)) * 1000000); /* Output Frequency */
+
+		apb_clk = (output_freq / ((pclk_div * 2) + 2));
+	} else {
+		u8 M, N, P;
+
+		N = scu_hpll & 0x0000001F;         /* 0:4   */
+		M = (scu_hpll & 0x00001FE0) >> 5;  /* 5:12  */
+		P = (scu_hpll & 0x0007E000) >> 13; /* 13:18 */
+		output_freq = ((24 * ((M+1) / (N+1) / (P+1))) * 1000000); /* Output Frequency */
+
+		apb_clk = (output_freq / ((pclk_div * 4) + 4));
+	}
+	return apb_clk;
+}
+
 static int aspeed_i2c_probe_bus(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
-- 
2.7.4



More information about the openbmc mailing list