xref: /openbmc/linux/drivers/fsi/fsi-master-i2cr.c (revision 6fa24b41)
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 
24 static 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 
39 static 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 
51 static 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 
64 static u32 i2cr_get_command(u32 address, bool parity)
65 {
66 	address <<= 1;
67 
68 	if (i2cr_check_parity32(address, parity))
69 		address |= 1;
70 
71 	return address;
72 }
73 
74 static 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(client->adapter, msgs, 2);
89 	if (ret == 2)
90 		return 0;
91 
92 	trace_i2cr_i2c_error(client, command, ret);
93 
94 	if (ret < 0)
95 		return ret;
96 
97 	return -EIO;
98 }
99 
100 static int i2cr_check_status(struct i2c_client *client)
101 {
102 	u64 status;
103 	int ret;
104 
105 	ret = i2cr_transfer(client, I2CR_STATUS_CMD, &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, &error);
115 		i2cr_transfer(client, I2CR_LOG_CMD, &log);
116 
117 		trace_i2cr_status_error(client, status, error, log);
118 
119 		buf[0] = I2CR_STATUS_CMD;
120 		i2c_master_send(client, (const char *)buf, sizeof(buf));
121 
122 		buf[0] = I2CR_ERROR_CMD;
123 		i2c_master_send(client, (const char *)buf, sizeof(buf));
124 
125 		buf[0] = I2CR_LOG_CMD;
126 		i2c_master_send(client, (const char *)buf, 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 
137 int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data)
138 {
139 	u32 command = i2cr_get_command(addr, I2CR_INITIAL_PARITY);
140 	int ret;
141 
142 	mutex_lock(&i2cr->lock);
143 
144 	ret = i2cr_transfer(i2cr->client, command, data);
145 	if (ret)
146 		goto unlock;
147 
148 	ret = i2cr_check_status(i2cr->client);
149 	if (ret)
150 		goto unlock;
151 
152 	trace_i2cr_read(i2cr->client, command, data);
153 
154 unlock:
155 	mutex_unlock(&i2cr->lock);
156 	return ret;
157 }
158 EXPORT_SYMBOL_GPL(fsi_master_i2cr_read);
159 
160 int 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(addr, i2cr_check_parity64(data));
166 	memcpy(&buf[1], &data, sizeof(data));
167 
168 	mutex_lock(&i2cr->lock);
169 
170 	ret = i2c_master_send(i2cr->client, (const char *)buf, sizeof(buf));
171 	if (ret == sizeof(buf)) {
172 		ret = i2cr_check_status(i2cr->client);
173 		if (!ret)
174 			trace_i2cr_write(i2cr->client, buf[0], data);
175 	} else {
176 		trace_i2cr_i2c_error(i2cr->client, buf[0], ret);
177 
178 		if (ret >= 0)
179 			ret = -EIO;
180 	}
181 
182 	mutex_unlock(&i2cr->lock);
183 	return ret;
184 }
185 EXPORT_SYMBOL_GPL(fsi_master_i2cr_write);
186 
187 static 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 
226 static 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 
250 static 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(dev->of_node);
255 
256 	kfree(i2cr);
257 }
258 
259 static int i2cr_probe(struct i2c_client *client)
260 {
261 	struct fsi_master_i2cr *i2cr;
262 	int ret;
263 
264 	i2cr = kzalloc(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(&i2cr->master.dev, "i2cr%d", i2cr->master.idx);
271 	i2cr->master.dev.parent = &client->dev;
272 	i2cr->master.dev.of_node = of_node_get(dev_of_node(&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(&i2cr->master);
283 	if (ret)
284 		return ret;
285 
286 	i2c_set_clientdata(client, i2cr);
287 	return 0;
288 }
289 
290 static void i2cr_remove(struct i2c_client *client)
291 {
292 	struct fsi_master_i2cr *i2cr = i2c_get_clientdata(client);
293 
294 	fsi_master_unregister(&i2cr->master);
295 }
296 
297 static const struct of_device_id i2cr_ids[] = {
298 	{ .compatible = "ibm,i2cr-fsi-master" },
299 	{ }
300 };
301 MODULE_DEVICE_TABLE(of, i2cr_ids);
302 
303 static 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 
312 module_i2c_driver(i2cr_driver)
313 
314 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
315 MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
316 MODULE_LICENSE("GPL");
317