[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