lkml.org 
[lkml]   [2023]   [Sep]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 2/2] i2c: rcar: improve accuracy for R-Car Gen3+
    Date
    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: Wolfram Sang <wsa+renesas@sang-engineering.com>
    ---

    Change since v2:
    * swapped SCHD/SCLD to match ordering of datasheets

    drivers/i2c/busses/i2c-rcar.c | 128 +++++++++++++++++++++++-----------
    1 file changed, 89 insertions(+), 39 deletions(-)

    diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
    index 6800059514bc..8417d5bc662b 100644
    --- a/drivers/i2c/busses/i2c-rcar.c
    +++ b/drivers/i2c/busses/i2c-rcar.c
    @@ -41,6 +41,10 @@
    #define ICSAR 0x1C /* slave address */
    #define ICMAR 0x20 /* master address */
    #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 ICDMAER 0x3c /* DMA enable (Gen3) */

    @@ -84,11 +88,25 @@
    #define RMDMAE BIT(1) /* DMA Master Received 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 */
    #define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */

    #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_DATA (MDBS | MIE)
    #define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
    @@ -128,6 +146,8 @@ struct rcar_i2c_priv {

    int pos;
    u32 icccr;
    + u16 schd;
    + u16 scld;
    u8 recovery_icmcr; /* protected by adapter lock */
    enum rcar_i2c_type devtype;
    struct i2c_client *slave;
    @@ -216,11 +236,16 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
    rcar_i2c_write(priv, ICMCR, MDBS);
    rcar_i2c_write(priv, ICMSR, 0);
    /* start clock */
    - rcar_i2c_write(priv, ICCCR, priv->icccr);
    -
    - if (priv->devtype == I2C_RCAR_GEN3)
    + if (priv->devtype < I2C_RCAR_GEN3) {
    + rcar_i2c_write(priv, ICCCR, priv->icccr);
    + } else {
    + 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);
    -
    + }
    }

    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)
    {
    - u32 scgd, cdf, round, ick, sum, scl, cdf_width;
    + u32 cdf, round, ick, sum, scl, cdf_width;
    unsigned long rate;
    struct device *dev = rcar_i2c_priv_to_dev(priv);
    struct i2c_timings t = {
    @@ -254,27 +279,17 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
    /* Fall back to previously used values if not supplied */
    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
    * see
    - * ICCCR
    + * ICCCR (and ICCCR2 for Gen3+)
    *
    * ick = clkp / (1 + CDF)
    * 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
    * ticf : I2C SCL falling time
    * tr : I2C SCL rising time
    @@ -284,11 +299,12 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
    */
    rate = clk_get_rate(priv->clk);
    cdf = rate / 20000000;
    - if (cdf >= 1U << cdf_width) {
    - dev_err(dev, "Input clock %lu too high\n", rate);
    - return -EIO;
    - }
    - ick = rate / (cdf + 1);
    + cdf_width = (priv->devtype == I2C_RCAR_GEN1) ? 2 : 3;
    + if (cdf >= 1U << cdf_width)
    + goto err_no_val;
    +
    + /* 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.
    @@ -301,24 +317,58 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
    round = DIV_ROUND_CLOSEST(ick, 1000000);
    round = DIV_ROUND_CLOSEST(round * sum, 1000);

    - /*
    - * SCL = ick / (20 + 8 * SCGD + F[(ticf + tr + intd) * ick])
    - * 20 + 8 * SCGD + F[...] = ick / SCL
    - * SCGD = ((ick / SCL) - 20 - F[...]) / 8
    - * Result (= SCL) should be less than bus_speed for hardware safety
    - */
    - scgd = DIV_ROUND_UP(ick, t.bus_freq_hz ?: 1);
    - scgd = DIV_ROUND_UP(scgd - 20 - round, 8);
    - scl = ick / (20 + 8 * scgd + round);
    + if (priv->devtype < I2C_RCAR_GEN3) {
    + u32 scgd;
    + /*
    + * SCL = ick / (20 + 8 * SCGD + F[(ticf + tr + intd) * ick])
    + * 20 + 8 * SCGD + F[...] = ick / SCL
    + * SCGD = ((ick / SCL) - 20 - F[...]) / 8
    + * Result (= SCL) should be less than bus_speed for hardware safety
    + */
    + scgd = DIV_ROUND_UP(ick, t.bus_freq_hz ?: 1);
    + scgd = DIV_ROUND_UP(scgd - 20 - round, 8);
    + scl = ick / (20 + 8 * scgd + round);

    - if (scgd > 0x3f)
    - goto err_no_val;
    + if (scgd > 0x3f)
    + goto err_no_val;

    - dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u, SCGD: %u\n",
    - scl, t.bus_freq_hz, rate, round, cdf, scgd);
    + dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u, SCGD: %u\n",
    + 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;

    --
    2.35.1
    \
     
     \ /
      Last update: 2023-09-22 00:00    [W:3.749 / U:0.812 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site