diff mbox series

[RFC,07/11] i2c: tegra: config settings for interface timings

Message ID 20240506225139.57647-8-kyarlagadda@nvidia.com
State Deferred
Headers show
Series Introduce Tegra register config settings | expand

Commit Message

Krishna Yarlagadda May 6, 2024, 10:51 p.m. UTC
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 mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index b3dc2603db35..263fd64e440f 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 = "high";
+		break;
+	case I2C_MAX_FAST_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_PLUS_FREQ:
+		name = "fastplus";
+		break;
+	case I2C_MAX_STANDARD_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_FREQ:
+		name = "fast";
+		break;
+	case 0 ... I2C_MAX_STANDARD_MODE_FREQ:
+	default:
+		name = "standard";
+		break;
+	}
+
+	tegra_i2c_write_cfg_settings(i2c_dev, "common");
+	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);