1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (C) IBM Corporation 2023 */
3
4#include <linux/device.h>
5#include <linux/fsi.h>
6#include <linux/i2c.h>
7#include <linux/module.h>
8#include <linux/mod_devicetable.h>
9#include <linux/mutex.h>
10
11#include "fsi-master-i2cr.h"
12
13#define CREATE_TRACE_POINTS
14#include <trace/events/fsi_master_i2cr.h>
15
16#define I2CR_ADDRESS_CFAM(a) ((a) >> 2)
17#define I2CR_INITIAL_PARITY true
18
19#define I2CR_STATUS_CMD 0x60002
20#define I2CR_STATUS_ERR BIT_ULL(61)
21#define I2CR_ERROR_CMD 0x60004
22#define I2CR_LOG_CMD 0x60008
23
24static const u8 i2cr_cfam[] = {
25 0xc0, 0x02, 0x0d, 0xa6,
26 0x80, 0x01, 0x10, 0x02,
27 0x80, 0x01, 0x10, 0x02,
28 0x80, 0x01, 0x10, 0x02,
29 0x80, 0x01, 0x80, 0x52,
30 0x80, 0x01, 0x10, 0x02,
31 0x80, 0x01, 0x10, 0x02,
32 0x80, 0x01, 0x10, 0x02,
33 0x80, 0x01, 0x10, 0x02,
34 0x80, 0x01, 0x22, 0x2d,
35 0x00, 0x00, 0x00, 0x00,
36 0xde, 0xad, 0xc0, 0xde
37};
38
39static bool i2cr_check_parity32(u32 v, bool parity)
40{
41 u32 i;
42
43 for (i = 0; i < 32; ++i) {
44 if (v & (1u << i))
45 parity = !parity;
46 }
47
48 return parity;
49}
50
51static bool i2cr_check_parity64(u64 v)
52{
53 u32 i;
54 bool parity = I2CR_INITIAL_PARITY;
55
56 for (i = 0; i < 64; ++i) {
57 if (v & (1llu << i))
58 parity = !parity;
59 }
60
61 return parity;
62}
63
64static u32 i2cr_get_command(u32 address, bool parity)
65{
66 address <<= 1;
67
68 if (i2cr_check_parity32(v: address, parity))
69 address |= 1;
70
71 return address;
72}
73
74static int i2cr_transfer(struct i2c_client *client, u32 command, u64 *data)
75{
76 struct i2c_msg msgs[2];
77 int ret;
78
79 msgs[0].addr = client->addr;
80 msgs[0].flags = 0;
81 msgs[0].len = sizeof(command);
82 msgs[0].buf = (__u8 *)&command;
83 msgs[1].addr = client->addr;
84 msgs[1].flags = I2C_M_RD;
85 msgs[1].len = sizeof(*data);
86 msgs[1].buf = (__u8 *)data;
87
88 ret = i2c_transfer(adap: client->adapter, msgs, num: 2);
89 if (ret == 2)
90 return 0;
91
92 trace_i2cr_i2c_error(client, command, rc: ret);
93
94 if (ret < 0)
95 return ret;
96
97 return -EIO;
98}
99
100static int i2cr_check_status(struct i2c_client *client)
101{
102 u64 status;
103 int ret;
104
105 ret = i2cr_transfer(client, I2CR_STATUS_CMD, data: &status);
106 if (ret)
107 return ret;
108
109 if (status & I2CR_STATUS_ERR) {
110 u32 buf[3] = { 0, 0, 0 };
111 u64 error;
112 u64 log;
113
114 i2cr_transfer(client, I2CR_ERROR_CMD, data: &error);
115 i2cr_transfer(client, I2CR_LOG_CMD, data: &log);
116
117 trace_i2cr_status_error(client, status, error, log);
118
119 buf[0] = I2CR_STATUS_CMD;
120 i2c_master_send(client, buf: (const char *)buf, count: sizeof(buf));
121
122 buf[0] = I2CR_ERROR_CMD;
123 i2c_master_send(client, buf: (const char *)buf, count: sizeof(buf));
124
125 buf[0] = I2CR_LOG_CMD;
126 i2c_master_send(client, buf: (const char *)buf, count: sizeof(buf));
127
128 dev_err(&client->dev, "status:%016llx error:%016llx log:%016llx\n", status, error,
129 log);
130 return -EREMOTEIO;
131 }
132
133 trace_i2cr_status(client, status);
134 return 0;
135}
136
137int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data)
138{
139 u32 command = i2cr_get_command(address: addr, I2CR_INITIAL_PARITY);
140 int ret;
141
142 mutex_lock(&i2cr->lock);
143
144 ret = i2cr_transfer(client: i2cr->client, command, data);
145 if (ret)
146 goto unlock;
147
148 ret = i2cr_check_status(client: i2cr->client);
149 if (ret)
150 goto unlock;
151
152 trace_i2cr_read(client: i2cr->client, command, data);
153
154unlock:
155 mutex_unlock(lock: &i2cr->lock);
156 return ret;
157}
158EXPORT_SYMBOL_GPL(fsi_master_i2cr_read);
159
160int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data)
161{
162 u32 buf[3] = { 0 };
163 int ret;
164
165 buf[0] = i2cr_get_command(address: addr, parity: i2cr_check_parity64(v: data));
166 memcpy(&buf[1], &data, sizeof(data));
167
168 mutex_lock(&i2cr->lock);
169
170 ret = i2c_master_send(client: i2cr->client, buf: (const char *)buf, count: sizeof(buf));
171 if (ret == sizeof(buf)) {
172 ret = i2cr_check_status(client: i2cr->client);
173 if (!ret)
174 trace_i2cr_write(client: i2cr->client, command: buf[0], data);
175 } else {
176 trace_i2cr_i2c_error(client: i2cr->client, command: buf[0], rc: ret);
177
178 if (ret >= 0)
179 ret = -EIO;
180 }
181
182 mutex_unlock(lock: &i2cr->lock);
183 return ret;
184}
185EXPORT_SYMBOL_GPL(fsi_master_i2cr_write);
186
187static int i2cr_read(struct fsi_master *master, int link, uint8_t id, uint32_t addr, void *val,
188 size_t size)
189{
190 struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
191 u64 data;
192 size_t i;
193 int ret;
194
195 if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
196 return -EINVAL;
197
198 /*
199 * The I2CR doesn't have CFAM or FSI slave address space - only the
200 * engines. In order for this to work with the FSI core, we need to
201 * emulate at minimum the CFAM config table so that the appropriate
202 * engines are discovered.
203 */
204 if (addr < 0xc00) {
205 if (addr > sizeof(i2cr_cfam) - 4)
206 addr = (addr & 0x3) + (sizeof(i2cr_cfam) - 4);
207
208 memcpy(val, &i2cr_cfam[addr], size);
209 return 0;
210 }
211
212 ret = fsi_master_i2cr_read(i2cr, I2CR_ADDRESS_CFAM(addr), &data);
213 if (ret)
214 return ret;
215
216 /*
217 * FSI core expects up to 4 bytes BE back, while I2CR replied with LE
218 * bytes on the wire.
219 */
220 for (i = 0; i < size; ++i)
221 ((u8 *)val)[i] = ((u8 *)&data)[7 - i];
222
223 return 0;
224}
225
226static int i2cr_write(struct fsi_master *master, int link, uint8_t id, uint32_t addr,
227 const void *val, size_t size)
228{
229 struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
230 u64 data = 0;
231 size_t i;
232
233 if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
234 return -EINVAL;
235
236 /* I2CR writes to CFAM or FSI slave address are a successful no-op. */
237 if (addr < 0xc00)
238 return 0;
239
240 /*
241 * FSI core passes up to 4 bytes BE, while the I2CR expects LE bytes on
242 * the wire.
243 */
244 for (i = 0; i < size; ++i)
245 ((u8 *)&data)[7 - i] = ((u8 *)val)[i];
246
247 return fsi_master_i2cr_write(i2cr, I2CR_ADDRESS_CFAM(addr), data);
248}
249
250static void i2cr_release(struct device *dev)
251{
252 struct fsi_master_i2cr *i2cr = to_fsi_master_i2cr(to_fsi_master(dev));
253
254 of_node_put(node: dev->of_node);
255
256 kfree(objp: i2cr);
257}
258
259static int i2cr_probe(struct i2c_client *client)
260{
261 struct fsi_master_i2cr *i2cr;
262 int ret;
263
264 i2cr = kzalloc(size: sizeof(*i2cr), GFP_KERNEL);
265 if (!i2cr)
266 return -ENOMEM;
267
268 /* Only one I2CR on any given I2C bus (fixed I2C device address) */
269 i2cr->master.idx = client->adapter->nr;
270 dev_set_name(dev: &i2cr->master.dev, name: "i2cr%d", i2cr->master.idx);
271 i2cr->master.dev.parent = &client->dev;
272 i2cr->master.dev.of_node = of_node_get(node: dev_of_node(dev: &client->dev));
273 i2cr->master.dev.release = i2cr_release;
274
275 i2cr->master.n_links = 1;
276 i2cr->master.read = i2cr_read;
277 i2cr->master.write = i2cr_write;
278
279 mutex_init(&i2cr->lock);
280 i2cr->client = client;
281
282 ret = fsi_master_register(master: &i2cr->master);
283 if (ret)
284 return ret;
285
286 i2c_set_clientdata(client, data: i2cr);
287 return 0;
288}
289
290static void i2cr_remove(struct i2c_client *client)
291{
292 struct fsi_master_i2cr *i2cr = i2c_get_clientdata(client);
293
294 fsi_master_unregister(master: &i2cr->master);
295}
296
297static const struct of_device_id i2cr_ids[] = {
298 { .compatible = "ibm,i2cr-fsi-master" },
299 { }
300};
301MODULE_DEVICE_TABLE(of, i2cr_ids);
302
303static struct i2c_driver i2cr_driver = {
304 .probe = i2cr_probe,
305 .remove = i2cr_remove,
306 .driver = {
307 .name = "fsi-master-i2cr",
308 .of_match_table = i2cr_ids,
309 },
310};
311
312module_i2c_driver(i2cr_driver)
313
314MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
315MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
316MODULE_LICENSE("GPL");
317

source code of linux/drivers/fsi/fsi-master-i2cr.c