Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/fsi/fsi-master-i2cr.c
26278 views
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 <[email protected]>");
315
MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
316
MODULE_LICENSE("GPL");
317
318