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