[RFC linux dev-4.7 3/7] drivers: i2c: Add port structure to FSI algorithm

Eddie James eajames at linux.vnet.ibm.com
Fri Mar 24 08:44:35 AEDT 2017


From: "Edward A. James" <eajames at us.ibm.com>

Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports are defined in the devicetree.

Signed-off-by: Edward A. James <eajames at us.ibm.com>
---
 drivers/i2c/busses/i2c-fsi.c | 94 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 93 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 11ab2e8..ad9159e 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -134,6 +134,14 @@ struct fsi_i2c_master {
 	struct fsi_device	*fsi;
 	u8			fifo_size;
 	unsigned long		clk;
+	struct list_head	ports;
+};
+
+struct fsi_i2c_port {
+	struct list_head	list;
+	struct i2c_adapter	adapter;
+	struct fsi_i2c_master	*master;
+	u16			port;
 };
 
 static unsigned long fsi_i2c_get_clk_div(struct fsi_i2c_master *i2c)
@@ -183,9 +191,46 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
 	return rc;
 }
 
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+	int rc;
+	struct fsi_device *fsi = port->master->fsi;
+	u32 mode, dummy = 0;
+	u16 old_port;
+
+	rc = fsi_device_read(fsi, I2C_FSI_MODE, &mode, sizeof(mode));
+	if (rc)
+		return rc;
+
+	old_port = GETFIELD(I2C_MODE_PORT, mode);
+
+	if (old_port != port->port) {
+		mode = SETFIELD(I2C_MODE_PORT, mode, port->port);
+		rc = fsi_device_write(fsi, I2C_FSI_MODE, &mode,
+				      sizeof(mode));
+		if (rc)
+			return rc;
+
+		/* reset engine when port is changed */
+		rc = fsi_device_write(fsi, I2C_FSI_RESET_ERR, &dummy,
+				      sizeof(dummy));
+		if (rc)
+			return rc;
+	}
+
+	return rc;
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 			int num)
 {
+	int rc;
+	struct fsi_i2c_port *port = adap->algo_data;
+
+	rc = fsi_i2c_set_port(port);
+	if (rc)
+		return rc;
+
 	return -ENOSYS;
 }
 
@@ -207,9 +252,10 @@ static const char *compatible_strings[] = {
 static int fsi_i2c_probe(struct device *dev)
 {
 	struct fsi_i2c_master *i2c;
+	struct fsi_i2c_port *port;
 	int rc, i;
 	struct device_node *np, *node = NULL;
-	u32 addr, clk;
+	u32 addr, clk, bus;
 
 	i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
 	if (!i2c)
@@ -217,6 +263,7 @@ static int fsi_i2c_probe(struct device *dev)
 
 	i2c->fsi = to_fsi_dev(dev);
 	i2c->clk = I2C_DEFAULT_CLK;
+	INIT_LIST_HEAD(&i2c->ports);
 
 	/* find node that matches FSI address */
 	for (i = 0; i < ARRAY_SIZE(compatible_strings) && !node; ++i) {
@@ -235,17 +282,61 @@ static int fsi_i2c_probe(struct device *dev)
 	if (node) {
 		if (!of_property_read_u32(node, "clock-frequency", &clk))
 			i2c->clk = clk;
+
+		/* add adapter for each i2c port of the master */
+		for_each_child_of_node(node, np) {
+			rc = of_property_read_u32(np, "reg", &bus);
+			if (rc || bus > 0xFFFF)
+				continue;
+
+			port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+			if (!port)
+				return -ENOMEM;
+
+			port->master = i2c;
+			port->port = bus;
+
+			port->adapter.owner = THIS_MODULE;
+			port->adapter.dev.parent = dev;
+			port->adapter.algo = &fsi_i2c_algorithm;
+			port->adapter.algo_data = port;
+
+			snprintf(port->adapter.name,
+				 sizeof(port->adapter.name),
+				 "fsi_i2c-%u", bus);
+
+			rc = i2c_add_adapter(&port->adapter);
+			if (rc < 0) {
+				devm_kfree(dev, port);
+				return rc;
+			}
+
+			list_add(&port->list, &i2c->ports);
+		}
 	}
 
 	rc = fsi_i2c_dev_init(i2c);
 	if (rc)
 		return rc;
 
+
 	dev_set_drvdata(dev, i2c);
 
 	return 0;
 }
 
+static int fsi_i2c_remove(struct device *dev)
+{
+	struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+	struct fsi_i2c_port *port;
+
+	list_for_each_entry(port, &i2c->ports, list) {
+		i2c_del_adapter(&port->adapter);
+	}
+
+	return 0;
+}
+
 static const struct fsi_device_id fsi_i2c_ids[] = {
 	{ FSI_ENGID_I2C_FSI, FSI_VERSION_ANY },
 	{ 0 }
@@ -257,6 +348,7 @@ static struct fsi_driver fsi_i2c_driver = {
 		.name = "power_i2cm",
 		.bus = &fsi_bus_type,
 		.probe = fsi_i2c_probe,
+		.remove = fsi_i2c_remove,
 	},
 };
 
-- 
1.8.3.1



More information about the openbmc mailing list