blob: 20f66bfa7279dc7f05ca9e6fa776100b150d949e [file] [log] [blame]
Edward A. James7bd86372017-05-15 12:28:44 -05001From patchwork Wed May 10 15:52:38 2017
2Content-Type: text/plain; charset="utf-8"
3MIME-Version: 1.0
4Content-Transfer-Encoding: 7bit
5Subject: [linux, dev-4.10, v2,
6 2/6] drivers: i2c: Add port structure to FSI algorithm
7From: eajames@linux.vnet.ibm.com
8X-Patchwork-Id: 760696
9Message-Id: <1494431562-25101-3-git-send-email-eajames@linux.vnet.ibm.com>
10To: openbmc@lists.ozlabs.org
11Cc: "Edward A. James" <eajames@us.ibm.com>, cbostic@linux.vnet.ibm.com
12Date: Wed, 10 May 2017 10:52:38 -0500
13
14From: "Edward A. James" <eajames@us.ibm.com>
15
16Add and initialize I2C adapters for each port on the FSI-attached I2C
17master. Ports are defined in the devicetree.
18
19Signed-off-by: Edward A. James <eajames@us.ibm.com>
20---
21 drivers/i2c/busses/i2c-fsi.c | 113 ++++++++++++++++++++++++++++++++++++++++++-
22 1 file changed, 112 insertions(+), 1 deletion(-)
23
24diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
25index 3c1087d..cdebc99 100644
26--- a/drivers/i2c/busses/i2c-fsi.c
27+++ b/drivers/i2c/busses/i2c-fsi.c
28@@ -30,6 +30,7 @@
29 #define SETFIELD(m, v, val) \
30 (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
31
32+#define I2C_MASTER_NR_OFFSET 100
33 #define I2C_DEFAULT_CLK_DIV 6
34
35 /* i2c registers */
36@@ -131,9 +132,21 @@
37
38 struct fsi_i2c_master {
39 struct fsi_device *fsi;
40+ int idx;
41 u8 fifo_size;
42+ struct list_head ports;
43+ struct ida ida;
44 };
45
46+struct fsi_i2c_port {
47+ struct list_head list;
48+ struct i2c_adapter adapter;
49+ struct fsi_i2c_master *master;
50+ u16 port;
51+};
52+
53+static DEFINE_IDA(fsi_i2c_ida);
54+
55 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
56 u32 *data)
57 {
58@@ -188,9 +201,44 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
59 return rc;
60 }
61
62+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
63+{
64+ int rc;
65+ struct fsi_device *fsi = port->master->fsi;
66+ u32 mode, dummy = 0;
67+ u16 old_port;
68+
69+ rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
70+ if (rc)
71+ return rc;
72+
73+ old_port = GETFIELD(I2C_MODE_PORT, mode);
74+
75+ if (old_port != port->port) {
76+ mode = SETFIELD(I2C_MODE_PORT, mode, port->port);
77+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
78+ if (rc)
79+ return rc;
80+
81+ /* reset engine when port is changed */
82+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
83+ if (rc)
84+ return rc;
85+ }
86+
87+ return rc;
88+}
89+
90 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
91 int num)
92 {
93+ int rc;
94+ struct fsi_i2c_port *port = adap->algo_data;
95+
96+ rc = fsi_i2c_set_port(port);
97+ if (rc)
98+ return rc;
99+
100 return -ENOSYS;
101 }
102
103@@ -207,13 +255,59 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
104 static int fsi_i2c_probe(struct device *dev)
105 {
106 struct fsi_i2c_master *i2c;
107- int rc;
108+ struct fsi_i2c_port *port;
109+ struct device_node *np;
110+ int rc, idx;
111+ u32 port_no;
112
113 i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
114 if (!i2c)
115 return -ENOMEM;
116
117 i2c->fsi = to_fsi_dev(dev);
118+ i2c->idx = ida_simple_get(&fsi_i2c_ida, 1, INT_MAX, GFP_KERNEL);
119+ ida_init(&i2c->ida);
120+ INIT_LIST_HEAD(&i2c->ports);
121+
122+ if (dev->of_node) {
123+ /* add adapter for each i2c port of the master */
124+ for_each_child_of_node(dev->of_node, np) {
125+ rc = of_property_read_u32(np, "port", &port_no);
126+ if (rc || port_no > 0xFFFF)
127+ continue;
128+
129+ /* make sure we don't overlap index with a buggy dts */
130+ idx = ida_simple_get(&i2c->ida, port_no,
131+ port_no + 1, GFP_KERNEL);
132+ if (idx < 0)
133+ continue;
134+
135+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
136+ if (!port)
137+ return -ENOMEM;
138+
139+ port->master = i2c;
140+ port->port = (u16)port_no;
141+
142+ port->adapter.owner = THIS_MODULE;
143+ port->adapter.dev.parent = dev;
144+ port->adapter.algo = &fsi_i2c_algorithm;
145+ port->adapter.algo_data = port;
146+ /* number ports uniquely */
147+ port->adapter.nr = (i2c->idx * I2C_MASTER_NR_OFFSET) +
148+ port_no;
149+
150+ snprintf(port->adapter.name,
151+ sizeof(port->adapter.name),
152+ "fsi_i2c-%u", port_no);
153+
154+ rc = i2c_add_numbered_adapter(&port->adapter);
155+ if (rc < 0)
156+ return rc;
157+
158+ list_add(&port->list, &i2c->ports);
159+ }
160+ }
161
162 rc = fsi_i2c_dev_init(i2c);
163 if (rc)
164@@ -224,6 +318,22 @@ static int fsi_i2c_probe(struct device *dev)
165 return 0;
166 }
167
168+static int fsi_i2c_remove(struct device *dev)
169+{
170+ struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
171+ struct fsi_i2c_port *port;
172+
173+ list_for_each_entry(port, &i2c->ports, list) {
174+ i2c_del_adapter(&port->adapter);
175+ }
176+
177+ ida_destroy(&i2c->ida);
178+
179+ ida_simple_remove(&fsi_i2c_ida, i2c->idx);
180+
181+ return 0;
182+}
183+
184 static const struct fsi_device_id fsi_i2c_ids[] = {
185 { FSI_ENGID_I2C_FSI, FSI_VERSION_ANY },
186 { 0 }
187@@ -235,6 +345,7 @@ static int fsi_i2c_probe(struct device *dev)
188 .name = "i2c_master_fsi",
189 .bus = &fsi_bus_type,
190 .probe = fsi_i2c_probe,
191+ .remove = fsi_i2c_remove,
192 },
193 };
194