Commit 54c76ed3 authored by Wolfram Sang's avatar Wolfram Sang Committed by Wolfram Sang

i2c: rcar: improve accuracy for R-Car Gen3+

With some new registers, SCL can be calculated to be closer to the
desired rate. Apply the new formula for R-Car Gen3 device types.
Signed-off-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 0e864b55
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
#define ICSAR 0x1C /* slave address */ #define ICSAR 0x1C /* slave address */
#define ICMAR 0x20 /* master address */ #define ICMAR 0x20 /* master address */
#define ICRXTX 0x24 /* data port */ #define ICRXTX 0x24 /* data port */
#define ICCCR2 0x28 /* Clock control 2 */
#define ICMPR 0x2C /* SCL mask control */
#define ICHPR 0x30 /* SCL HIGH control */
#define ICLPR 0x34 /* SCL LOW control */
#define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */ #define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */
#define ICDMAER 0x3c /* DMA enable (Gen3) */ #define ICDMAER 0x3c /* DMA enable (Gen3) */
...@@ -84,11 +88,25 @@ ...@@ -84,11 +88,25 @@
#define RMDMAE BIT(1) /* DMA Master Received Enable */ #define RMDMAE BIT(1) /* DMA Master Received Enable */
#define TMDMAE BIT(0) /* DMA Master Transmitted Enable */ #define TMDMAE BIT(0) /* DMA Master Transmitted Enable */
/* ICCCR2 */
#define CDFD BIT(2) /* CDF Disable */
#define HLSE BIT(1) /* HIGH/LOW Separate Control Enable */
#define SME BIT(0) /* SCL Mask Enable */
/* ICFBSCR */ /* ICFBSCR */
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */ #define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
#define RCAR_MIN_DMA_LEN 8 #define RCAR_MIN_DMA_LEN 8
/* SCL low/high ratio 5:4 to meet all I2C timing specs (incl safety margin) */
#define RCAR_SCLD_RATIO 5
#define RCAR_SCHD_RATIO 4
/*
* SMD should be smaller than SCLD/SCHD and is always around 20 in the docs.
* Thus, we simply use 20 which works for low and high speeds.
*/
#define RCAR_DEFAULT_SMD 20
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG) #define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
#define RCAR_BUS_PHASE_DATA (MDBS | MIE) #define RCAR_BUS_PHASE_DATA (MDBS | MIE)
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB) #define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
...@@ -128,6 +146,8 @@ struct rcar_i2c_priv { ...@@ -128,6 +146,8 @@ struct rcar_i2c_priv {
int pos; int pos;
u32 icccr; u32 icccr;
u16 schd;
u16 scld;
u8 recovery_icmcr; /* protected by adapter lock */ u8 recovery_icmcr; /* protected by adapter lock */
enum rcar_i2c_type devtype; enum rcar_i2c_type devtype;
struct i2c_client *slave; struct i2c_client *slave;
...@@ -216,11 +236,16 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) ...@@ -216,11 +236,16 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICMCR, MDBS); rcar_i2c_write(priv, ICMCR, MDBS);
rcar_i2c_write(priv, ICMSR, 0); rcar_i2c_write(priv, ICMSR, 0);
/* start clock */ /* start clock */
if (priv->devtype < I2C_RCAR_GEN3) {
rcar_i2c_write(priv, ICCCR, priv->icccr); rcar_i2c_write(priv, ICCCR, priv->icccr);
} else {
if (priv->devtype == I2C_RCAR_GEN3) rcar_i2c_write(priv, ICCCR2, CDFD | HLSE | SME);
rcar_i2c_write(priv, ICCCR, priv->icccr);
rcar_i2c_write(priv, ICMPR, RCAR_DEFAULT_SMD);
rcar_i2c_write(priv, ICHPR, priv->schd);
rcar_i2c_write(priv, ICLPR, priv->scld);
rcar_i2c_write(priv, ICFBSCR, TCYC17); rcar_i2c_write(priv, ICFBSCR, TCYC17);
}
} }
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
...@@ -241,7 +266,7 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) ...@@ -241,7 +266,7 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
{ {
u32 scgd, cdf, round, ick, sum, scl, cdf_width; u32 cdf, round, ick, sum, scl, cdf_width;
unsigned long rate; unsigned long rate;
struct device *dev = rcar_i2c_priv_to_dev(priv); struct device *dev = rcar_i2c_priv_to_dev(priv);
struct i2c_timings t = { struct i2c_timings t = {
...@@ -254,27 +279,17 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) ...@@ -254,27 +279,17 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
/* Fall back to previously used values if not supplied */ /* Fall back to previously used values if not supplied */
i2c_parse_fw_timings(dev, &t, false); i2c_parse_fw_timings(dev, &t, false);
switch (priv->devtype) {
case I2C_RCAR_GEN1:
cdf_width = 2;
break;
case I2C_RCAR_GEN2:
case I2C_RCAR_GEN3:
cdf_width = 3;
break;
default:
dev_err(dev, "device type error\n");
return -EIO;
}
/* /*
* calculate SCL clock * calculate SCL clock
* see * see
* ICCCR * ICCCR (and ICCCR2 for Gen3+)
* *
* ick = clkp / (1 + CDF) * ick = clkp / (1 + CDF)
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick]) * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
* *
* for Gen3+:
* SCL = clkp / (8 + SMD * 2 + SCLD + SCHD +F[(ticf + tr + intd) * clkp])
*
* ick : I2C internal clock < 20 MHz * ick : I2C internal clock < 20 MHz
* ticf : I2C SCL falling time * ticf : I2C SCL falling time
* tr : I2C SCL rising time * tr : I2C SCL rising time
...@@ -284,11 +299,12 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) ...@@ -284,11 +299,12 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
*/ */
rate = clk_get_rate(priv->clk); rate = clk_get_rate(priv->clk);
cdf = rate / 20000000; cdf = rate / 20000000;
if (cdf >= 1U << cdf_width) { cdf_width = (priv->devtype == I2C_RCAR_GEN1) ? 2 : 3;
dev_err(dev, "Input clock %lu too high\n", rate); if (cdf >= 1U << cdf_width)
return -EIO; goto err_no_val;
}
ick = rate / (cdf + 1); /* On Gen3+, we use cdf only for the filters, not as a SCL divider */
ick = rate / (priv->devtype < I2C_RCAR_GEN3 ? (cdf + 1) : 1);
/* /*
* It is impossible to calculate a large scale number on u32. Separate it. * It is impossible to calculate a large scale number on u32. Separate it.
...@@ -301,6 +317,8 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) ...@@ -301,6 +317,8 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
round = DIV_ROUND_CLOSEST(ick, 1000000); round = DIV_ROUND_CLOSEST(ick, 1000000);
round = DIV_ROUND_CLOSEST(round * sum, 1000); round = DIV_ROUND_CLOSEST(round * sum, 1000);
if (priv->devtype < I2C_RCAR_GEN3) {
u32 scgd;
/* /*
* SCL = ick / (20 + 8 * SCGD + F[(ticf + tr + intd) * ick]) * SCL = ick / (20 + 8 * SCGD + F[(ticf + tr + intd) * ick])
* 20 + 8 * SCGD + F[...] = ick / SCL * 20 + 8 * SCGD + F[...] = ick / SCL
...@@ -317,8 +335,40 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) ...@@ -317,8 +335,40 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u, SCGD: %u\n", dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u, SCGD: %u\n",
scl, t.bus_freq_hz, rate, round, cdf, scgd); scl, t.bus_freq_hz, rate, round, cdf, scgd);
/* keep icccr value */
priv->icccr = scgd << cdf_width | cdf; priv->icccr = scgd << cdf_width | cdf;
} else {
u32 x, sum_ratio = RCAR_SCHD_RATIO + RCAR_SCLD_RATIO;
/*
* SCLD/SCHD ratio and SMD default value are explained above
* where they are defined. With these definitions, we can compute
* x as a base value for the SCLD/SCHD ratio:
*
* SCL = clkp / (8 + 2 * SMD + SCLD + SCHD + F[(ticf + tr + intd) * clkp])
* SCL = clkp / (8 + 2 * RCAR_DEFAULT_SMD + RCAR_SCLD_RATIO * x
* + RCAR_SCHD_RATIO * x + F[...])
*
* with: sum_ratio = RCAR_SCLD_RATIO + RCAR_SCHD_RATIO
* and: smd = RCAR_DEFAULT_SMD
*
* SCL = clkp / (8 + 2 * smd + sum_ratio * x + F[...])
* 8 + 2 * smd + sum_ratio * x + F[...] = clkp / SCL
* x = ((clkp / SCL) - 8 - 2 * smd - F[...]) / sum_ratio
*/
x = DIV_ROUND_UP(rate, t.bus_freq_hz ?: 1);
x = DIV_ROUND_UP(x - 8 - 2 * RCAR_DEFAULT_SMD - round, sum_ratio);
scl = rate / (8 + 2 * RCAR_DEFAULT_SMD + sum_ratio * x + round);
/* Bail out if values don't fit into 16 bit or SMD became too large */
if (x * RCAR_SCLD_RATIO > 0xffff || RCAR_DEFAULT_SMD > x * RCAR_SCHD_RATIO)
goto err_no_val;
priv->icccr = cdf;
priv->schd = RCAR_SCHD_RATIO * x;
priv->scld = RCAR_SCLD_RATIO * x;
dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u SCHD %u SCLD %u\n",
scl, t.bus_freq_hz, rate, round, cdf, priv->schd, priv->scld);
}
return 0; return 0;
......
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