1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) IBM Corporation 2023 */
4 #include <linux/device.h>
7 #include <linux/module.h>
8 #include <linux/mod_devicetable.h>
9 #include <linux/mutex.h>
11 #include "fsi-master-i2cr.h"
13 #define CREATE_TRACE_POINTS
14 #include <trace/events/fsi_master_i2cr.h>
16 #define I2CR_ADDRESS_CFAM(a) ((a) >> 2)
17 #define I2CR_INITIAL_PARITY true
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
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
39 static bool i2cr_check_parity32(u32 v
, bool parity
)
43 for (i
= 0; i
< 32; ++i
) {
51 static bool i2cr_check_parity64(u64 v
)
54 bool parity
= I2CR_INITIAL_PARITY
;
56 for (i
= 0; i
< 64; ++i
) {
64 static u32
i2cr_get_command(u32 address
, bool parity
)
68 if (i2cr_check_parity32(address
, parity
))
74 static int i2cr_transfer(struct i2c_client
*client
, u32 command
, u64
*data
)
76 struct i2c_msg msgs
[2];
79 msgs
[0].addr
= client
->addr
;
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
;
88 ret
= i2c_transfer(client
->adapter
, msgs
, 2);
92 trace_i2cr_i2c_error(client
, command
, ret
);
100 static int i2cr_check_status(struct i2c_client
*client
)
105 ret
= i2cr_transfer(client
, I2CR_STATUS_CMD
, &status
);
109 if (status
& I2CR_STATUS_ERR
) {
110 u32 buf
[3] = { 0, 0, 0 };
114 i2cr_transfer(client
, I2CR_ERROR_CMD
, &error
);
115 i2cr_transfer(client
, I2CR_LOG_CMD
, &log
);
117 trace_i2cr_status_error(client
, status
, error
, log
);
119 buf
[0] = I2CR_STATUS_CMD
;
120 i2c_master_send(client
, (const char *)buf
, sizeof(buf
));
122 buf
[0] = I2CR_ERROR_CMD
;
123 i2c_master_send(client
, (const char *)buf
, sizeof(buf
));
125 buf
[0] = I2CR_LOG_CMD
;
126 i2c_master_send(client
, (const char *)buf
, sizeof(buf
));
128 dev_err(&client
->dev
, "status:%016llx error:%016llx log:%016llx\n", status
, error
,
133 trace_i2cr_status(client
, status
);
137 int fsi_master_i2cr_read(struct fsi_master_i2cr
*i2cr
, u32 addr
, u64
*data
)
139 u32 command
= i2cr_get_command(addr
, I2CR_INITIAL_PARITY
);
142 mutex_lock(&i2cr
->lock
);
144 ret
= i2cr_transfer(i2cr
->client
, command
, data
);
148 ret
= i2cr_check_status(i2cr
->client
);
152 trace_i2cr_read(i2cr
->client
, command
, data
);
155 mutex_unlock(&i2cr
->lock
);
158 EXPORT_SYMBOL_GPL(fsi_master_i2cr_read
);
160 int fsi_master_i2cr_write(struct fsi_master_i2cr
*i2cr
, u32 addr
, u64 data
)
165 buf
[0] = i2cr_get_command(addr
, i2cr_check_parity64(data
));
166 memcpy(&buf
[1], &data
, sizeof(data
));
168 mutex_lock(&i2cr
->lock
);
170 ret
= i2c_master_send(i2cr
->client
, (const char *)buf
, sizeof(buf
));
171 if (ret
== sizeof(buf
)) {
172 ret
= i2cr_check_status(i2cr
->client
);
174 trace_i2cr_write(i2cr
->client
, buf
[0], data
);
176 trace_i2cr_i2c_error(i2cr
->client
, buf
[0], ret
);
182 mutex_unlock(&i2cr
->lock
);
185 EXPORT_SYMBOL_GPL(fsi_master_i2cr_write
);
187 static int i2cr_read(struct fsi_master
*master
, int link
, uint8_t id
, uint32_t addr
, void *val
,
190 struct fsi_master_i2cr
*i2cr
= container_of(master
, struct fsi_master_i2cr
, master
);
195 if (link
|| id
|| (addr
& 0xffff0000) || !(size
== 1 || size
== 2 || size
== 4))
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.
205 if (addr
> sizeof(i2cr_cfam
) - 4)
206 addr
= (addr
& 0x3) + (sizeof(i2cr_cfam
) - 4);
208 memcpy(val
, &i2cr_cfam
[addr
], size
);
212 ret
= fsi_master_i2cr_read(i2cr
, I2CR_ADDRESS_CFAM(addr
), &data
);
217 * FSI core expects up to 4 bytes BE back, while I2CR replied with LE
220 for (i
= 0; i
< size
; ++i
)
221 ((u8
*)val
)[i
] = ((u8
*)&data
)[7 - i
];
226 static int i2cr_write(struct fsi_master
*master
, int link
, uint8_t id
, uint32_t addr
,
227 const void *val
, size_t size
)
229 struct fsi_master_i2cr
*i2cr
= container_of(master
, struct fsi_master_i2cr
, master
);
233 if (link
|| id
|| (addr
& 0xffff0000) || !(size
== 1 || size
== 2 || size
== 4))
236 /* I2CR writes to CFAM or FSI slave address are a successful no-op. */
241 * FSI core passes up to 4 bytes BE, while the I2CR expects LE bytes on
244 for (i
= 0; i
< size
; ++i
)
245 ((u8
*)&data
)[7 - i
] = ((u8
*)val
)[i
];
247 return fsi_master_i2cr_write(i2cr
, I2CR_ADDRESS_CFAM(addr
), data
);
250 static void i2cr_release(struct device
*dev
)
252 struct fsi_master_i2cr
*i2cr
= to_fsi_master_i2cr(to_fsi_master(dev
));
254 of_node_put(dev
->of_node
);
259 static int i2cr_probe(struct i2c_client
*client
)
261 struct fsi_master_i2cr
*i2cr
;
264 i2cr
= kzalloc(sizeof(*i2cr
), GFP_KERNEL
);
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
;
275 i2cr
->master
.n_links
= 1;
276 i2cr
->master
.read
= i2cr_read
;
277 i2cr
->master
.write
= i2cr_write
;
279 mutex_init(&i2cr
->lock
);
280 i2cr
->client
= client
;
282 ret
= fsi_master_register(&i2cr
->master
);
286 i2c_set_clientdata(client
, i2cr
);
290 static void i2cr_remove(struct i2c_client
*client
)
292 struct fsi_master_i2cr
*i2cr
= i2c_get_clientdata(client
);
294 fsi_master_unregister(&i2cr
->master
);
297 static const struct of_device_id i2cr_ids
[] = {
298 { .compatible
= "ibm,i2cr-fsi-master" },
301 MODULE_DEVICE_TABLE(of
, i2cr_ids
);
303 static struct i2c_driver i2cr_driver
= {
305 .remove
= i2cr_remove
,
307 .name
= "fsi-master-i2cr",
308 .of_match_table
= i2cr_ids
,
312 module_i2c_driver(i2cr_driver
)
314 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
315 MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
316 MODULE_LICENSE("GPL");