]> git.ipfire.org Git - thirdparty/u-boot.git/blobdiff - drivers/i2c/designware_i2c.c
treewide: Remove clk_free
[thirdparty/u-boot.git] / drivers / i2c / designware_i2c.c
index e1d5aeb19db9e670219053bd021269aa7e2ec43d..29cf63375c7f173e71c8265f69ddd041c8fbc4f2 100644 (file)
@@ -1,31 +1,40 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2009
- * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
+ * Vipin Kumar, STMicroelectronics, vipin.kumar@st.com.
  */
 
 #include <common.h>
 #include <clk.h>
 #include <dm.h>
 #include <i2c.h>
+#include <log.h>
 #include <malloc.h>
 #include <pci.h>
 #include <reset.h>
 #include <asm/io.h>
+#include <linux/delay.h>
 #include "designware_i2c.h"
 #include <dm/device_compat.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
-static int  dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
-{
-       u32 ena = enable ? IC_ENABLE_0B : 0;
+/*
+ * This assigned unique hex value is constant and is derived from the two ASCII
+ * letters 'DW' followed by a 16-bit unsigned number
+ */
+#define DW_I2C_COMP_TYPE       0x44570140
 
-       writel(ena, &i2c_base->ic_enable);
+/*
+ * This constant is used to calculate when during the clock high phase the data
+ * bit shall be read. The value was copied from the Linux v6.5 function
+ * i2c_dw_scl_hcnt() which provides the following explanation:
+ *
+ * "This is just an experimental rule: the tHD;STA period turned out to be
+ * proportinal to (_HCNT + 3). With this setting, we could meet both tHIGH and
+ * tHD;STA timing specs."
+ */
+#define T_HD_STA_OFFSET 3
 
-       return 0;
-}
-#else
 static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
 {
        u32 ena = enable ? IC_ENABLE_0B : 0;
@@ -47,7 +56,6 @@ static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
 
        return -ETIMEDOUT;
 }
-#endif
 
 /* High and low times in different speed modes (in ns) */
 enum {
@@ -60,7 +68,7 @@ enum {
  *
  * @ic_clk: Input clock in Hz
  * @period_ns: Period to represent, in ns
- * @return calculated count
+ * Return: calculated count
  */
 static uint calc_counts(uint ic_clk, uint period_ns)
 {
@@ -126,7 +134,7 @@ static const struct i2c_mode_info info_for_mode[] = {
  * @ic_clk: IC clock speed in Hz
  * @spk_cnt: Spike-suppression count
  * @config: Returns value to use
- * @return 0 if OK, -EINVAL if the calculation failed due to invalid data
+ * Return: 0 if OK, -EINVAL if the calculation failed due to invalid data
  */
 static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
                              int ic_clk, int spk_cnt,
@@ -152,34 +160,34 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
        min_tlow_cnt = calc_counts(ic_clk, info->min_scl_lowtime_ns);
        min_thigh_cnt = calc_counts(ic_clk, info->min_scl_hightime_ns);
 
-       debug("dw_i2c: period %d rise %d fall %d tlow %d thigh %d spk %d\n",
-             period_cnt, rise_cnt, fall_cnt, min_tlow_cnt, min_thigh_cnt,
-             spk_cnt);
+       debug("dw_i2c: mode %d, ic_clk %d, speed %d, period %d rise %d fall %d tlow %d thigh %d spk %d\n",
+             mode, ic_clk, info->speed, period_cnt, rise_cnt, fall_cnt,
+             min_tlow_cnt, min_thigh_cnt, spk_cnt);
 
        /*
         * Back-solve for hcnt and lcnt according to the following equations:
-        * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time
+        * SCL_High_time = [(HCNT + IC_*_SPKLEN + T_HD_STA_OFFSET) * ic_clk] + SCL_Fall_time
         * SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time
         */
-       hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt;
+       hcnt = min_thigh_cnt - fall_cnt - T_HD_STA_OFFSET - spk_cnt;
        lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1;
 
        if (hcnt < 0 || lcnt < 0) {
                debug("dw_i2c: bad counts. hcnt = %d lcnt = %d\n", hcnt, lcnt);
-               return -EINVAL;
+               return log_msg_ret("counts", -EINVAL);
        }
 
        /*
         * Now add things back up to ensure the period is hit. If it is off,
         * split the difference and bias to lcnt for remainder
         */
-       tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1;
+       tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1;
 
        if (tot < period_cnt) {
                diff = (period_cnt - tot) / 2;
                hcnt += diff;
                lcnt += diff;
-               tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1;
+               tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1;
                lcnt += period_cnt - tot;
        }
 
@@ -197,11 +205,20 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
        return 0;
 }
 
-static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
-                         struct dw_i2c_speed_config *config)
+/**
+ * calc_bus_speed() - Calculate the config to use for a particular i2c speed
+ *
+ * @priv: Private information for the driver (NULL if not using driver model)
+ * @i2c_base: Registers for the I2C controller
+ * @speed: Required i2c speed in Hz
+ * @bus_clk: Input clock to the I2C controller in Hz (e.g. IC_CLK)
+ * @config: Returns the config to use for this speed
+ * Return: 0 if OK, -ve on error
+ */
+static int calc_bus_speed(struct dw_i2c *priv, struct i2c_regs *regs, int speed,
+                         ulong bus_clk, struct dw_i2c_speed_config *config)
 {
        const struct dw_scl_sda_cfg *scl_sda_cfg = NULL;
-       struct i2c_regs *regs = priv->regs;
        enum i2c_speed_mode i2c_spd;
        int spk_cnt;
        int ret;
@@ -209,16 +226,25 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
        if (priv)
                scl_sda_cfg = priv->scl_sda_cfg;
        /* Allow high speed if there is no config, or the config allows it */
-       if (speed >= I2C_SPEED_HIGH_RATE &&
-           (!scl_sda_cfg || scl_sda_cfg->has_high_speed))
+       if (speed >= I2C_SPEED_HIGH_RATE)
                i2c_spd = IC_SPEED_MODE_HIGH;
-       else if (speed >= I2C_SPEED_FAST_RATE)
-               i2c_spd = IC_SPEED_MODE_FAST_PLUS;
        else if (speed >= I2C_SPEED_FAST_PLUS_RATE)
+               i2c_spd = IC_SPEED_MODE_FAST_PLUS;
+       else if (speed >= I2C_SPEED_FAST_RATE)
                i2c_spd = IC_SPEED_MODE_FAST;
        else
                i2c_spd = IC_SPEED_MODE_STANDARD;
 
+       /* Check is high speed possible and fall back to fast mode if not */
+       if (i2c_spd == IC_SPEED_MODE_HIGH) {
+               u32 comp_param1;
+
+               comp_param1 = readl(&regs->comp_param1);
+               if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
+                               != DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH)
+                       i2c_spd = IC_SPEED_MODE_FAST;
+       }
+
        /* Get the proper spike-suppression count based on target speed */
        if (!priv || !priv->has_spk_cnt)
                spk_cnt = 0;
@@ -231,6 +257,9 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
                if (i2c_spd == IC_SPEED_MODE_STANDARD) {
                        config->scl_hcnt = scl_sda_cfg->ss_hcnt;
                        config->scl_lcnt = scl_sda_cfg->ss_lcnt;
+               } else if (i2c_spd == IC_SPEED_MODE_HIGH) {
+                       config->scl_hcnt = scl_sda_cfg->hs_hcnt;
+                       config->scl_lcnt = scl_sda_cfg->hs_lcnt;
                } else {
                        config->scl_hcnt = scl_sda_cfg->fs_hcnt;
                        config->scl_lcnt = scl_sda_cfg->fs_lcnt;
@@ -246,11 +275,14 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
        return 0;
 }
 
-/*
- * _dw_i2c_set_bus_speed - Set the i2c speed
- * @speed:     required i2c speed
+/**
+ * _dw_i2c_set_bus_speed() - Set the i2c speed
  *
- * Set the i2c speed.
+ * @priv: Private information for the driver (NULL if not using driver model)
+ * @i2c_base: Registers for the I2C controller
+ * @speed: Required i2c speed in Hz
+ * @bus_clk: Input clock to the I2C controller in Hz (e.g. IC_CLK)
+ * Return: 0 if OK, -ve on error
  */
 static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
                                 unsigned int speed, unsigned int bus_clk)
@@ -260,7 +292,7 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
        unsigned int ena;
        int ret;
 
-       ret = calc_bus_speed(priv, speed, bus_clk, &config);
+       ret = calc_bus_speed(priv, i2c_base, speed, bus_clk, &config);
        if (ret)
                return ret;
 
@@ -274,7 +306,7 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
 
        switch (config.speed_mode) {
        case IC_SPEED_MODE_HIGH:
-               cntl |= IC_CON_SPD_SS;
+               cntl |= IC_CON_SPD_HS;
                writel(config.scl_hcnt, &i2c_base->ic_hs_scl_hcnt);
                writel(config.scl_lcnt, &i2c_base->ic_hs_scl_lcnt);
                break;
@@ -301,6 +333,32 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
        /* Restore back i2c now speed set */
        if (ena == IC_ENABLE_0B)
                dw_i2c_enable(i2c_base, true);
+       if (priv)
+               priv->config = config;
+
+       return 0;
+}
+
+int dw_i2c_gen_speed_config(const struct udevice *dev, int speed_hz,
+                           struct dw_i2c_speed_config *config)
+{
+       struct dw_i2c *priv = dev_get_priv(dev);
+       ulong rate;
+       int ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+       rate = clk_get_rate(&priv->clk);
+       if (IS_ERR_VALUE(rate))
+               return log_msg_ret("clk", -EINVAL);
+#else
+       rate = IC_CLK;
+#endif
+
+       ret = calc_bus_speed(priv, priv->regs, speed_hz, rate, config);
+       if (ret)
+               printf("%s: ret=%d\n", __func__, ret);
+       if (ret)
+               return log_msg_ret("calc_bus_speed", ret);
 
        return 0;
 }
@@ -540,7 +598,7 @@ static int __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
        writel(IC_RX_TL, &i2c_base->ic_rx_tl);
        writel(IC_TX_TL, &i2c_base->ic_tx_tl);
        writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
-#ifndef CONFIG_DM_I2C
+#if !CONFIG_IS_ENABLED(DM_I2C)
        _dw_i2c_set_bus_speed(NULL, i2c_base, speed, IC_CLK);
        writel(slaveaddr, &i2c_base->ic_sar);
 #endif
@@ -553,7 +611,7 @@ static int __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
        return 0;
 }
 
-#ifndef CONFIG_DM_I2C
+#if !CONFIG_IS_ENABLED(DM_I2C)
 /*
  * The legacy I2C functions. These need to get removed once
  * all users of this driver are converted to DM.
@@ -627,24 +685,6 @@ U_BOOT_I2C_ADAP_COMPLETE(dw_0, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
                         dw_i2c_write, dw_i2c_set_bus_speed,
                         CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 0)
 
-#if CONFIG_SYS_I2C_BUS_MAX >= 2
-U_BOOT_I2C_ADAP_COMPLETE(dw_1, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
-                        dw_i2c_write, dw_i2c_set_bus_speed,
-                        CONFIG_SYS_I2C_SPEED1, CONFIG_SYS_I2C_SLAVE1, 1)
-#endif
-
-#if CONFIG_SYS_I2C_BUS_MAX >= 3
-U_BOOT_I2C_ADAP_COMPLETE(dw_2, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
-                        dw_i2c_write, dw_i2c_set_bus_speed,
-                        CONFIG_SYS_I2C_SPEED2, CONFIG_SYS_I2C_SLAVE2, 2)
-#endif
-
-#if CONFIG_SYS_I2C_BUS_MAX >= 4
-U_BOOT_I2C_ADAP_COMPLETE(dw_3, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
-                        dw_i2c_write, dw_i2c_set_bus_speed,
-                        CONFIG_SYS_I2C_SPEED3, CONFIG_SYS_I2C_SLAVE3, 3)
-#endif
-
 #else /* CONFIG_DM_I2C */
 /* The DM I2C functions */
 
@@ -681,7 +721,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 #if CONFIG_IS_ENABLED(CLK)
        rate = clk_get_rate(&i2c->clk);
        if (IS_ERR_VALUE(rate))
-               return -EINVAL;
+               return log_ret(-EINVAL);
 #else
        rate = IC_CLK;
 #endif
@@ -704,22 +744,24 @@ static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
        return ret;
 }
 
-int designware_i2c_ofdata_to_platdata(struct udevice *bus)
+int designware_i2c_of_to_plat(struct udevice *bus)
 {
        struct dw_i2c *priv = dev_get_priv(bus);
        int ret;
 
        if (!priv->regs)
-               priv->regs = (struct i2c_regs *)devfdt_get_addr_ptr(bus);
+               priv->regs = dev_read_addr_ptr(bus);
        dev_read_u32(bus, "i2c-scl-rising-time-ns", &priv->scl_rise_time_ns);
        dev_read_u32(bus, "i2c-scl-falling-time-ns", &priv->scl_fall_time_ns);
        dev_read_u32(bus, "i2c-sda-hold-time-ns", &priv->sda_hold_time_ns);
 
        ret = reset_get_bulk(bus, &priv->resets);
-       if (ret)
-               dev_warn(bus, "Can't get reset: %d\n", ret);
-       else
+       if (ret) {
+               if (ret != -ENOTSUPP)
+                       dev_warn(bus, "Can't get reset: %d\n", ret);
+       } else {
                reset_deassert_bulk(&priv->resets);
+       }
 
 #if CONFIG_IS_ENABLED(CLK)
        ret = clk_get_by_index(bus, 0, &priv->clk);
@@ -728,7 +770,6 @@ int designware_i2c_ofdata_to_platdata(struct udevice *bus)
 
        ret = clk_enable(&priv->clk);
        if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
-               clk_free(&priv->clk);
                dev_err(bus, "failed to enable clock\n");
                return ret;
        }
@@ -740,6 +781,17 @@ int designware_i2c_ofdata_to_platdata(struct udevice *bus)
 int designware_i2c_probe(struct udevice *bus)
 {
        struct dw_i2c *priv = dev_get_priv(bus);
+       uint comp_type;
+
+       comp_type = readl(&priv->regs->comp_type);
+       if (comp_type != DW_I2C_COMP_TYPE) {
+               log_err("I2C bus %s has unknown type %#x\n", bus->name,
+                       comp_type);
+               return -ENXIO;
+       }
+
+       log_debug("I2C bus %s version %#x\n", bus->name,
+                 readl(&priv->regs->comp_version));
 
        return __dw_i2c_init(priv->regs, 0, 0);
 }
@@ -750,7 +802,6 @@ int designware_i2c_remove(struct udevice *dev)
 
 #if CONFIG_IS_ENABLED(CLK)
        clk_disable(&priv->clk);
-       clk_free(&priv->clk);
 #endif
 
        return reset_release_bulk(&priv->resets);
@@ -771,9 +822,9 @@ U_BOOT_DRIVER(i2c_designware) = {
        .name   = "i2c_designware",
        .id     = UCLASS_I2C,
        .of_match = designware_i2c_ids,
-       .ofdata_to_platdata = designware_i2c_ofdata_to_platdata,
+       .of_to_plat = designware_i2c_of_to_plat,
        .probe  = designware_i2c_probe,
-       .priv_auto_alloc_size = sizeof(struct dw_i2c),
+       .priv_auto      = sizeof(struct dw_i2c),
        .remove = designware_i2c_remove,
        .flags  = DM_FLAG_OS_PREPARE,
        .ops    = &designware_i2c_ops,