From ad115623449a3e80d89b725f833b885a9ad29c33 Mon Sep 17 00:00:00 2001
From: Federico Vaga <federico.vaga@cern.ch>
Date: Tue, 17 Mar 2020 11:13:35 +0100
Subject: [PATCH] sw:i2c: add support for kernel greater than 4.7

Instead of check for version here and there, the main code always uses
the latest API, and in a preprocessor ``if`` statement I implemented
the compatibility layer.

Like this it will be easier to apply patches from the kernel to our
local driver

Signed-off-by: Federico Vaga <federico.vaga@cern.ch>
---
 CHANGELOG.rst                                 |   1 +
 .../drivers/i2c/busses/i2c-ocores.c           | 167 +++++++++++++-----
 2 files changed, 128 insertions(+), 40 deletions(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 8b26d4eb..2af56bfc 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -14,6 +14,7 @@ Versioning: `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_
 Added
 -----
 - [hdl] VHDL functions to convert characters and strings to upper/lower case.
+- [sw][i2c] support for kernel greater than 4.7
 Changed
 -------
 - [hdl] Rewritten the WB master interface used in simulations.
diff --git a/software/i2c-ocores/drivers/i2c/busses/i2c-ocores.c b/software/i2c-ocores/drivers/i2c/busses/i2c-ocores.c
index b7a458d6..12465de3 100644
--- a/software/i2c-ocores/drivers/i2c/busses/i2c-ocores.c
+++ b/software/i2c-ocores/drivers/i2c/busses/i2c-ocores.c
@@ -28,6 +28,91 @@
 #include <linux/spinlock.h>
 #include <linux/jiffies.h>
 
+struct ocores_i2c;
+static int ohwr_i2c_mux_select(struct ocores_i2c *i2c, u32 num);
+static int ohwr_i2c_mux_deselect(struct ocores_i2c *i2c, u32 num);
+
+#if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE
+struct i2c_mux_core {
+	struct i2c_adapter *parent;
+	struct device *dev;
+	void *priv;
+
+	unsigned int max_adapters;
+	struct i2c_adapter *adapter[0];
+};
+
+/**
+ * It selects the I2C bus to use and lock it
+ */
+static int ocores_i2c_mux_select_old_api(struct i2c_adapter *adap,
+					 void *priv, u32 num)
+{
+	struct ocores_i2c *i2c = priv;
+
+	return ohwr_i2c_mux_select(i2c, num);
+}
+
+/**
+ * It unlocks the bus so that it can be changed.
+ */
+static int ocores_i2c_mux_deselect_old_api(struct i2c_adapter *adap,
+					   void *priv, u32 num)
+{
+	struct ocores_i2c *i2c = priv;
+
+	return ohwr_i2c_mux_deselect(i2c, num);
+}
+
+struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
+				   struct device *dev, int max_adapters,
+				   int sizeof_priv, u32 flags,
+				   int (*select)(struct i2c_mux_core *, u32),
+				   int (*deselect)(struct i2c_mux_core *, u32))
+{
+	struct i2c_mux_core *muxc;
+
+	muxc = devm_kzalloc(dev, sizeof(*muxc) +
+			    sizeof(*muxc->adapter) * max_adapters +
+			    sizeof_priv, GFP_KERNEL);
+	if (!muxc)
+		return NULL;
+	if (sizeof_priv)
+		muxc->priv = &muxc->adapter[max_adapters];
+	muxc->parent = parent;
+	muxc->dev = dev;
+	muxc->max_adapters = max_adapters;
+
+	return muxc;
+}
+
+int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
+			u32 force_nr, u32 chan_id,
+			unsigned int class)
+{
+	muxc->adapter[chan_id] = i2c_add_mux_adapter(muxc->parent,
+						     muxc->dev,
+						     muxc->priv,
+						     force_nr,
+						     chan_id,
+#if  KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+						     class, /* class */
+#endif
+						     ocores_i2c_mux_select_old_api,
+						     ocores_i2c_mux_deselect_old_api);
+	return muxc->adapter[chan_id] ? 0 : -EINVAL;
+}
+
+void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
+{
+	int i;
+
+	for (i = 0; i < muxc->max_adapters; ++i)
+		i2c_del_mux_adapter(muxc->adapter[i]);
+}
+#endif
+
+
 #define OCORES_FLAG_POLL BIT(0)
 
 /**
@@ -41,8 +126,7 @@ struct ocores_i2c {
 	unsigned long flags;
 	wait_queue_head_t wait;
 	struct i2c_adapter adap;
-	struct i2c_adapter **adap_mux;
-	unsigned int n_adap_mux;
+	struct i2c_mux_core *adap_mux;
 	struct i2c_msg *msg;
 	int pos;
 	int nmsgs;
@@ -578,14 +662,8 @@ static const struct platform_device_id ocores_id_table[] = {
 };
 MODULE_DEVICE_TABLE(id_table, ocores_id_table);
 
-
-/**
- * It selects the I2C bus to use and lock it
- */
-static int ocores_i2c_mux_select(struct i2c_adapter *adap,
-				 void *priv, u32 num)
+static int ohwr_i2c_mux_select(struct ocores_i2c *i2c, u32 num)
 {
-	struct ocores_i2c *i2c = priv;
 	u8 mux;
 
 	mux = oc_getreg(i2c, OCI2C_OHWR_MUX);
@@ -601,57 +679,69 @@ static int ocores_i2c_mux_select(struct i2c_adapter *adap,
 	return 0;
 }
 
-
-/**
- * It unlocks the bus so that it can be changed.
- */
-static int ocores_i2c_mux_deselect(struct i2c_adapter *adap,
-				   void *priv, u32 num)
+static int ohwr_i2c_mux_deselect(struct ocores_i2c *i2c, u32 num)
 {
-	struct ocores_i2c *i2c = priv;
 	u8 mux;
 
 	/* Unlock bus selection */
 	mux = oc_getreg(i2c, OCI2C_OHWR_MUX);
 	if (unlikely(!(mux & OCI2C_OHWR_MUX_BUSY)))
-		dev_err(&adap->dev, "deselect a bus that was not selected\n");
+		dev_err(i2c->adap_mux->dev,
+			"deselect a bus that was not selected\n");
 	mux &= ~OCI2C_OHWR_MUX_BUSY;
 	oc_setreg(i2c, OCI2C_OHWR_MUX, mux);
 
 	return 0;
 }
 
+/**
+ * It selects the I2C bus to use and lock it
+ */
+static int ocores_i2c_mux_select(struct i2c_mux_core *muxc, u32 num)
+{
+	struct ocores_i2c *i2c = muxc->priv;
+
+	return ohwr_i2c_mux_select(i2c, num);
+}
+
+/**
+ * It unlocks the bus so that it can be changed.
+ */
+static int ocores_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 num)
+{
+	struct ocores_i2c *i2c = muxc->priv;
+
+	return ohwr_i2c_mux_deselect(i2c, num);
+}
 
 /**
  * Add OHWR multiplexer
  */
 static int ocores_i2c_probe_ohwr(struct ocores_i2c *i2c)
 {
-	int err, i;
-
-	i2c->n_adap_mux = 2;
-	i2c->adap_mux = devm_kzalloc(&i2c->adap.dev,
-				     sizeof(void *) * i2c->n_adap_mux,
-				     GFP_KERNEL);
-	for (i = 0; i < i2c->n_adap_mux; ++i) {
-		i2c->adap_mux[i] = i2c_add_mux_adapter(&i2c->adap,
-						       i2c->adap.dev.parent,
-						       i2c,
-						       0, i,
-#if  KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
-						       0, /* class */
-#endif
-						       ocores_i2c_mux_select,
-						       ocores_i2c_mux_deselect);
-		if (!i2c->adap_mux[i])
+	int i, err = 0;
+
+	i2c->adap_mux = i2c_mux_alloc(&i2c->adap, i2c->adap.dev.parent,
+				      2, sizeof(i2c), 0,
+				      ocores_i2c_mux_select,
+				      ocores_i2c_mux_deselect);
+	if (!i2c->adap_mux) {
+		err = -ENOMEM;
+		goto err_exit;
+	}
+	i2c->adap_mux->priv = i2c;
+	for (i = 0; i < i2c->adap_mux->max_adapters; ++i) {
+		err = i2c_mux_add_adapter(i2c->adap_mux,
+					  0, i, 0);
+		if (err)
 			goto err_add;
 	}
 
 	return 0;
 
 err_add:
-	while (--i >= 0)
-		i2c_del_mux_adapter(i2c->adap_mux[i]);
+	i2c_mux_del_adapters(i2c->adap_mux);
+err_exit:
 	return err;
 }
 
@@ -660,10 +750,7 @@ err_add:
  */
 static void ocores_i2c_remove_ohwr(struct ocores_i2c *i2c)
 {
-	int i;
-
-	for (i = 0; i < i2c->n_adap_mux; ++i)
-		i2c_del_mux_adapter(i2c->adap_mux[i]);
+	i2c_mux_del_adapters(i2c->adap_mux);
 }
 
 #ifdef CONFIG_OF
-- 
GitLab