[RFC PATCH V2 07/12] i2c: tegra: config settings for interface timings

Krishna Yarlagadda
2024-07-01T15:13:41Z

Use config settings framework to initialize Tegra I2C interface
timing registers and clock divisor based on I2C speed modes.

Each speed mode uses predefined configuration for interface timing
and clock registers.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Signed-off-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
---
drivers/i2c/busses/i2c-tegra.c | 134 +++++++++++++++++++++++++++++++--
1 file changed, 129 insertions(+), 5 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index b3dc2603db35..b81925576060 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -28,6 +28,8 @@
#include <linux/pm_runtime.h>
#include <linux/reset.h>

+#include <soc/tegra/tegra-cfg.h>
+
#define BYTES_PER_FIFO_WORD 4

#define I2C_CNFG 0x000
@@ -108,8 +110,9 @@
#define I2C_MST_CORE_CLKEN_OVR BIT(0)

#define I2C_INTERFACE_TIMING_0 0x094
-#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8)
-#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0)
+#define I2C_INTERFACE_TIMING_THIGH GENMASK(15, 8)
+#define I2C_INTERFACE_TIMING_TLOW GENMASK(7, 0)
+
#define I2C_INTERFACE_TIMING_1 0x098
#define I2C_INTERFACE_TIMING_TBUF GENMASK(29, 24)
#define I2C_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
@@ -117,8 +120,9 @@
#define I2C_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)

#define I2C_HS_INTERFACE_TIMING_0 0x09c
-#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(13, 8)
-#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0)
+#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(15, 8)
+#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(7, 0)
+
#define I2C_HS_INTERFACE_TIMING_1 0x0a0
#define I2C_HS_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
#define I2C_HS_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
@@ -226,6 +230,49 @@ struct tegra_i2c_hw_feature {
bool has_interface_timing_reg;
};

+/**
+ * I2C register config fields.
+ */
+static const struct tegra_cfg_field_desc i2c_cfg_fields[] = {
+ TEGRA_CFG_FIELD("nvidia,i2c-clk-divisor-fs-mode",
+ I2C_CLK_DIVISOR, I2C_CLK_DIVISOR_STD_FAST_MODE),
+ TEGRA_CFG_FIELD("nvidia,i2c-clk-divisor-hs-mode",
+ I2C_CLK_DIVISOR, I2C_CLK_DIVISOR_HSMODE),
+ TEGRA_CFG_FIELD("nvidia,i2c-hs-sclk-high-period",
+ I2C_HS_INTERFACE_TIMING_0,
+ I2C_HS_INTERFACE_TIMING_THIGH),
+ TEGRA_CFG_FIELD("nvidia,i2c-hs-sclk-low-period",
+ I2C_HS_INTERFACE_TIMING_0,
+ I2C_HS_INTERFACE_TIMING_TLOW),
+ TEGRA_CFG_FIELD("nvidia,i2c-hs-stop-setup-time",
+ I2C_HS_INTERFACE_TIMING_1,
+ I2C_HS_INTERFACE_TIMING_TSU_STO),
+ TEGRA_CFG_FIELD("nvidia,i2c-hs-start-hold-time",
+ I2C_HS_INTERFACE_TIMING_1,
+ I2C_HS_INTERFACE_TIMING_THD_STA),
+ TEGRA_CFG_FIELD("nvidia,i2c-hs-start-setup-time",
+ I2C_HS_INTERFACE_TIMING_1,
+ I2C_HS_INTERFACE_TIMING_TSU_STA),
+ TEGRA_CFG_FIELD("nvidia,i2c-sclk-high-period",
+ I2C_INTERFACE_TIMING_0, I2C_INTERFACE_TIMING_THIGH),
+ TEGRA_CFG_FIELD("nvidia,i2c-sclk-low-period",
+ I2C_INTERFACE_TIMING_0, I2C_INTERFACE_TIMING_TLOW),
+ TEGRA_CFG_FIELD("nvidia,i2c-bus-free-time",
+ I2C_INTERFACE_TIMING_1, I2C_INTERFACE_TIMING_TBUF),
+ TEGRA_CFG_FIELD("nvidia,i2c-stop-setup-time",
+ I2C_INTERFACE_TIMING_1, I2C_INTERFACE_TIMING_TSU_STO),
+ TEGRA_CFG_FIELD("nvidia,i2c-start-hold-time",
+ I2C_INTERFACE_TIMING_1, I2C_INTERFACE_TIMING_THD_STA),
+ TEGRA_CFG_FIELD("nvidia,i2c-start-setup-time",
+ I2C_INTERFACE_TIMING_1, I2C_INTERFACE_TIMING_TSU_STA),
+};
+
+static struct tegra_cfg_desc i2c_cfg_desc = {
+ .num_regs = 0,
+ .num_fields = ARRAY_SIZE(i2c_cfg_fields),
+ .fields = i2c_cfg_fields,
+};
+
/**
* struct tegra_i2c_dev - per device I2C context
* @dev: device reference for power management
@@ -288,6 +335,8 @@ struct tegra_i2c_dev {
dma_addr_t dma_phys;
void *dma_buf;

+ struct tegra_cfg_list *list;
+
bool multimaster_mode;
bool atomic_mode;
bool dma_mode;
@@ -340,6 +389,16 @@ static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
return readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
}

+static void i2c_update(struct tegra_i2c_dev *i2c_dev, u32 mask,
+ u32 val, unsigned int reg)
+{
+ u32 rval;
+
+ rval = i2c_readl(i2c_dev, reg);
+ rval = (rval & ~mask) | val;
+ i2c_writel(i2c_dev, rval, reg);
+}
+
static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data,
unsigned int reg, unsigned int len)
{
@@ -604,6 +663,48 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
return 0;
}

+static void tegra_i2c_write_cfg_settings(struct tegra_i2c_dev *i2c_dev,
+ const char *name)
+{
+ struct tegra_cfg_reg *regs;
+ struct tegra_cfg *cfg;
+ unsigned int i;
+
+ cfg = tegra_cfg_get_by_name(i2c_dev->dev, i2c_dev->list, name);
+ if (!cfg)
+ return;
+
+ regs = cfg->regs;
+ for (i = 0; i < cfg->num_regs; i++) {
+ i2c_update(i2c_dev, regs[i].mask, regs[i].value,
+ regs[i].offset);
+ }
+}
+
+static void tegra_i2c_config_cfg_settings(struct tegra_i2c_dev *i2c_dev)
+{
+ const char *name;
+
+ switch (i2c_dev->timings.bus_freq_hz) {
+ case I2C_MAX_FAST_MODE_PLUS_FREQ + 1 ... I2C_MAX_HIGH_SPEED_MODE_FREQ:
+ name = "i2c-high-cfg";
+ break;
+ case I2C_MAX_FAST_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_PLUS_FREQ:
+ name = "i2c-fastplus-cfg";
+ break;
+ case I2C_MAX_STANDARD_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_FREQ:
+ name = "i2c-fast-cfg";
+ break;
+ case 0 ... I2C_MAX_STANDARD_MODE_FREQ:
+ default:
+ name = "i2c-standard-cfg";
+ break;
+ }
+
+ tegra_i2c_write_cfg_settings(i2c_dev, "i2c-common-cfg");
+ tegra_i2c_write_cfg_settings(i2c_dev, name);
+}
+
static void tegra_i2c_set_clk_params(struct tegra_i2c_dev *i2c_dev)
{
u32 val, clk_divisor, tsu_thd, tlow, thigh, non_hs_mode;
@@ -712,7 +813,11 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (IS_VI(i2c_dev))
tegra_i2c_vi_init(i2c_dev);

- tegra_i2c_set_clk_params(i2c_dev);
+ if (i2c_dev->list)
+ tegra_i2c_config_cfg_settings(i2c_dev);
+ else
+ tegra_i2c_set_clk_params(i2c_dev);
+
err = tegra_i2c_set_div_clk(i2c_dev);
if (err)
return err;
@@ -1772,6 +1877,8 @@ static int tegra_i2c_probe(struct platform_device *pdev)
struct tegra_i2c_dev *i2c_dev;
struct resource *res;
int err;
+ const struct tegra_cfg_field_desc *fields;
+ unsigned int count = 0, i, j;

i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
@@ -1808,6 +1915,23 @@ static int tegra_i2c_probe(struct platform_device *pdev)
if (err)
return err;

+ fields = i2c_cfg_fields;
+
+ for (i = 0; i < i2c_cfg_desc.num_fields; i++) {
+ for (j = 0; j < i; j++)
+ if (fields[i].offset == fields[j].offset)
+ break;
+ if (i == j)
+ count++;
+ }
+ i2c_cfg_desc.num_regs = count;
+
+ i2c_dev->list = tegra_cfg_get(i2c_dev->dev, NULL, &i2c_cfg_desc);
+ if (IS_ERR_OR_NULL(i2c_dev->list)) {
+ dev_dbg(&pdev->dev, "Config setting not available\n");
+ i2c_dev->list = NULL;
+ }
+
tegra_i2c_parse_dt(i2c_dev);

err = tegra_i2c_init_reset(i2c_dev);
--
2.43.2