1 // SPDX-License-Identifier: GPL-2.0-only
3 * I2C slave mode testunit
5 * Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
6 * Copyright (C) 2020 by Renesas Electronics Corporation
9 #include <linux/bitops.h>
10 #include <linux/i2c.h>
11 #include <linux/init.h>
12 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
17 #define TU_CUR_VERSION 0x01
20 TU_CMD_READ_BYTES
= 1, /* save 0 for ABORT, RESET or similar */
37 struct testunit_data
{
41 struct i2c_client
*client
;
42 struct delayed_work worker
;
45 static void i2c_slave_testunit_work(struct work_struct
*work
)
47 struct testunit_data
*tu
= container_of(work
, struct testunit_data
, worker
.work
);
52 msg
.addr
= I2C_CLIENT_END
;
55 switch (tu
->regs
[TU_REG_CMD
]) {
56 case TU_CMD_READ_BYTES
:
57 msg
.addr
= tu
->regs
[TU_REG_DATAL
];
59 msg
.len
= tu
->regs
[TU_REG_DATAH
];
62 case TU_CMD_HOST_NOTIFY
:
66 msgbuf
[0] = tu
->client
->addr
;
67 msgbuf
[1] = tu
->regs
[TU_REG_DATAL
];
68 msgbuf
[2] = tu
->regs
[TU_REG_DATAH
];
75 if (msg
.addr
!= I2C_CLIENT_END
) {
76 ret
= i2c_transfer(tu
->client
->adapter
, &msg
, 1);
77 /* convert '0 msgs transferred' to errno */
78 ret
= (ret
== 0) ? -EIO
: ret
;
82 dev_err(&tu
->client
->dev
, "CMD%02X failed (%d)\n", tu
->regs
[TU_REG_CMD
], ret
);
84 clear_bit(TU_FLAG_IN_PROCESS
, &tu
->flags
);
87 static int i2c_slave_testunit_slave_cb(struct i2c_client
*client
,
88 enum i2c_slave_event event
, u8
*val
)
90 struct testunit_data
*tu
= i2c_get_clientdata(client
);
94 case I2C_SLAVE_WRITE_RECEIVED
:
95 if (test_bit(TU_FLAG_IN_PROCESS
, &tu
->flags
))
98 if (tu
->reg_idx
< TU_NUM_REGS
)
99 tu
->regs
[tu
->reg_idx
] = *val
;
103 if (tu
->reg_idx
<= TU_NUM_REGS
)
106 /* TU_REG_CMD always written at this point */
107 if (tu
->regs
[TU_REG_CMD
] >= TU_NUM_CMDS
)
113 if (tu
->reg_idx
== TU_NUM_REGS
) {
114 set_bit(TU_FLAG_IN_PROCESS
, &tu
->flags
);
115 queue_delayed_work(system_long_wq
, &tu
->worker
,
116 msecs_to_jiffies(10 * tu
->regs
[TU_REG_DELAY
]));
120 case I2C_SLAVE_WRITE_REQUESTED
:
124 case I2C_SLAVE_READ_REQUESTED
:
125 case I2C_SLAVE_READ_PROCESSED
:
126 *val
= TU_CUR_VERSION
;
133 static int i2c_slave_testunit_probe(struct i2c_client
*client
)
135 struct testunit_data
*tu
;
137 tu
= devm_kzalloc(&client
->dev
, sizeof(struct testunit_data
), GFP_KERNEL
);
142 i2c_set_clientdata(client
, tu
);
143 INIT_DELAYED_WORK(&tu
->worker
, i2c_slave_testunit_work
);
145 return i2c_slave_register(client
, i2c_slave_testunit_slave_cb
);
148 static int i2c_slave_testunit_remove(struct i2c_client
*client
)
150 struct testunit_data
*tu
= i2c_get_clientdata(client
);
152 cancel_delayed_work_sync(&tu
->worker
);
153 i2c_slave_unregister(client
);
157 static const struct i2c_device_id i2c_slave_testunit_id
[] = {
158 { "slave-testunit", 0 },
161 MODULE_DEVICE_TABLE(i2c
, i2c_slave_testunit_id
);
163 static struct i2c_driver i2c_slave_testunit_driver
= {
165 .name
= "i2c-slave-testunit",
167 .probe_new
= i2c_slave_testunit_probe
,
168 .remove
= i2c_slave_testunit_remove
,
169 .id_table
= i2c_slave_testunit_id
,
171 module_i2c_driver(i2c_slave_testunit_driver
);
173 MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
174 MODULE_DESCRIPTION("I2C slave mode test unit");
175 MODULE_LICENSE("GPL v2");