blob: 50410ffc81d0cd112b5826c2292d29698a96454a [file] [log] [blame]
Edward A. James7bd86372017-05-15 12:28:44 -05001From patchwork Wed May 10 15:52:39 2017
2Content-Type: text/plain; charset="utf-8"
3MIME-Version: 1.0
4Content-Transfer-Encoding: 7bit
5Subject: [linux, dev-4.10, v2,
6 3/6] drivers: i2c: Add transfer implementation for FSI algorithm
7From: eajames@linux.vnet.ibm.com
8X-Patchwork-Id: 760702
9Message-Id: <1494431562-25101-4-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:39 -0500
13
14From: "Edward A. James" <eajames@us.ibm.com>
15
16Execute I2C transfers from the FSI-attached I2C master. Use polling
17instead of interrupts as we have no hardware IRQ over FSI.
18
19Signed-off-by: Edward A. James <eajames@us.ibm.com>
20---
21 drivers/i2c/busses/i2c-fsi.c | 193 ++++++++++++++++++++++++++++++++++++++++++-
22 1 file changed, 191 insertions(+), 2 deletions(-)
23
24diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
25index cdebc99..f690b16 100644
26--- a/drivers/i2c/busses/i2c-fsi.c
27+++ b/drivers/i2c/busses/i2c-fsi.c
28@@ -143,6 +143,7 @@ struct fsi_i2c_port {
29 struct i2c_adapter adapter;
30 struct fsi_i2c_master *master;
31 u16 port;
32+ u16 xfrd;
33 };
34
35 static DEFINE_IDA(fsi_i2c_ida);
36@@ -229,17 +230,205 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
37 return rc;
38 }
39
40+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
41+ bool stop)
42+{
43+ int rc;
44+ struct fsi_i2c_master *i2c = port->master;
45+ u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
46+
47+ port->xfrd = 0;
48+
49+ if (msg->flags & I2C_M_RD)
50+ cmd |= I2C_CMD_READ;
51+
52+ if (stop || msg->flags & I2C_M_STOP)
53+ cmd |= I2C_CMD_WITH_STOP;
54+
55+ cmd = SETFIELD(I2C_CMD_ADDR, cmd, msg->addr >> 1);
56+ cmd = SETFIELD(I2C_CMD_LEN, cmd, msg->len);
57+
58+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
59+
60+ return rc;
61+}
62+
63+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
64+ u8 fifo_count)
65+{
66+ int write;
67+ int rc = 0;
68+ struct fsi_i2c_master *i2c = port->master;
69+ int bytes_to_write = i2c->fifo_size - fifo_count;
70+ int bytes_remaining = msg->len - port->xfrd;
71+
72+ if (bytes_to_write > bytes_remaining)
73+ bytes_to_write = bytes_remaining;
74+
75+ while (bytes_to_write > 0) {
76+ write = bytes_to_write;
77+ /* fsi limited to max 4 byte aligned ops */
78+ if (bytes_to_write > 4)
79+ write = 4;
80+ else if (write == 3)
81+ write = 2;
82+
83+ rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
84+ &msg->buf[port->xfrd], write);
85+ if (rc)
86+ return rc;
87+
88+ port->xfrd += write;
89+ bytes_to_write -= write;
90+ }
91+
92+ return rc;
93+}
94+
95+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
96+ u8 fifo_count)
97+{
98+ int read;
99+ int rc = 0;
100+ struct fsi_i2c_master *i2c = port->master;
101+ int xfr_remaining = msg->len - port->xfrd;
102+ u32 dummy;
103+
104+ while (fifo_count) {
105+ read = fifo_count;
106+ /* fsi limited to max 4 byte aligned ops */
107+ if (fifo_count > 4)
108+ read = 4;
109+ else if (read == 3)
110+ read = 2;
111+
112+ if (xfr_remaining) {
113+ if (xfr_remaining < read)
114+ read = xfr_remaining;
115+
116+ rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
117+ &msg->buf[port->xfrd], read);
118+ if (rc)
119+ return rc;
120+
121+ port->xfrd += read;
122+ xfr_remaining -= read;
123+ } else {
124+ /* no more buffer but data in fifo, need to clear it */
125+ rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
126+ read);
127+ if (rc)
128+ return rc;
129+ }
130+
131+ fifo_count -= read;
132+ }
133+
134+ return rc;
135+}
136+
137+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
138+ struct i2c_msg *msg, u32 status)
139+{
140+ int rc;
141+ u8 fifo_count;
142+ struct fsi_i2c_master *i2c = port->master;
143+ u32 dummy = 0;
144+
145+ if (status & I2C_STAT_ERR) {
146+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
147+ if (rc)
148+ return rc;
149+
150+ if (status & I2C_STAT_NACK)
151+ return -EFAULT;
152+
153+ return -EIO;
154+ }
155+
156+ if (status & I2C_STAT_DAT_REQ) {
157+ fifo_count = GETFIELD(I2C_STAT_FIFO_COUNT, status);
158+
159+ if (msg->flags & I2C_M_RD)
160+ rc = fsi_i2c_read_fifo(port, msg, fifo_count);
161+ else
162+ rc = fsi_i2c_write_fifo(port, msg, fifo_count);
163+
164+ return rc;
165+ }
166+
167+ if (status & I2C_STAT_CMD_COMP) {
168+ if (port->xfrd < msg->len)
169+ rc = -ENODATA;
170+ else
171+ rc = msg->len;
172+ }
173+
174+ return rc;
175+}
176+
177+static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
178+ unsigned long timeout)
179+{
180+ const unsigned long local_timeout = 2; /* jiffies */
181+ u32 status = 0;
182+ int rc;
183+
184+ do {
185+ rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
186+ &status);
187+ if (rc)
188+ return rc;
189+
190+ if (status & I2C_STAT_ANY_RESP) {
191+ rc = fsi_i2c_handle_status(port, msg, status);
192+ if (rc < 0)
193+ return rc;
194+
195+ /* cmd complete and all data xfrd */
196+ if (rc == msg->len)
197+ return 0;
198+
199+ /* need to xfr more data, but maybe don't need wait */
200+ continue;
201+ }
202+
203+ set_current_state(TASK_UNINTERRUPTIBLE);
204+ schedule_timeout(local_timeout);
205+ timeout = (timeout < local_timeout) ? 0 :
206+ timeout - local_timeout;
207+ } while (timeout);
208+
209+ return -ETIME;
210+}
211+
212 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
213 int num)
214 {
215- int rc;
216+ int i, rc;
217+ unsigned long start_time;
218 struct fsi_i2c_port *port = adap->algo_data;
219+ struct i2c_msg *msg;
220
221 rc = fsi_i2c_set_port(port);
222 if (rc)
223 return rc;
224
225- return -ENOSYS;
226+ for (i = 0; i < num; ++i) {
227+ msg = msgs + i;
228+ start_time = jiffies;
229+
230+ rc = fsi_i2c_start(port, msg, i == num - 1);
231+ if (rc)
232+ return rc;
233+
234+ rc = fsi_i2c_wait(port, msg,
235+ adap->timeout - (jiffies - start_time));
236+ if (rc)
237+ return rc;
238+ }
239+
240+ return 0;
241 }
242
243 static u32 fsi_i2c_functionality(struct i2c_adapter *adap)