1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * I2C slave mode testunit 4 * 5 * Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> 6 * Copyright (C) 2020 by Renesas Electronics Corporation 7 */ 8 9 #include <linux/bitops.h> 10 #include <linux/i2c.h> 11 #include <linux/init.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/slab.h> 15 #include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */ 16 17 #define TU_CUR_VERSION 0x01 18 19 enum testunit_cmds { 20 TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */ 21 TU_CMD_HOST_NOTIFY, 22 TU_CMD_SMBUS_BLOCK_PROC_CALL, 23 TU_NUM_CMDS 24 }; 25 26 enum testunit_regs { 27 TU_REG_CMD, 28 TU_REG_DATAL, 29 TU_REG_DATAH, 30 TU_REG_DELAY, 31 TU_NUM_REGS 32 }; 33 34 enum testunit_flags { 35 TU_FLAG_IN_PROCESS, 36 }; 37 38 struct testunit_data { 39 unsigned long flags; 40 u8 regs[TU_NUM_REGS]; 41 u8 reg_idx; 42 struct i2c_client *client; 43 struct delayed_work worker; 44 }; 45 46 static void i2c_slave_testunit_work(struct work_struct *work) 47 { 48 struct testunit_data *tu = container_of(work, struct testunit_data, worker.work); 49 struct i2c_msg msg; 50 u8 msgbuf[256]; 51 int ret = 0; 52 53 msg.addr = I2C_CLIENT_END; 54 msg.buf = msgbuf; 55 56 switch (tu->regs[TU_REG_CMD]) { 57 case TU_CMD_READ_BYTES: 58 msg.addr = tu->regs[TU_REG_DATAL]; 59 msg.flags = I2C_M_RD; 60 msg.len = tu->regs[TU_REG_DATAH]; 61 break; 62 63 case TU_CMD_HOST_NOTIFY: 64 msg.addr = 0x08; 65 msg.flags = 0; 66 msg.len = 3; 67 msgbuf[0] = tu->client->addr; 68 msgbuf[1] = tu->regs[TU_REG_DATAL]; 69 msgbuf[2] = tu->regs[TU_REG_DATAH]; 70 break; 71 72 default: 73 break; 74 } 75 76 if (msg.addr != I2C_CLIENT_END) { 77 ret = i2c_transfer(tu->client->adapter, &msg, 1); 78 /* convert '0 msgs transferred' to errno */ 79 ret = (ret == 0) ? -EIO : ret; 80 } 81 82 if (ret < 0) 83 dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret); 84 85 clear_bit(TU_FLAG_IN_PROCESS, &tu->flags); 86 } 87 88 static int i2c_slave_testunit_slave_cb(struct i2c_client *client, 89 enum i2c_slave_event event, u8 *val) 90 { 91 struct testunit_data *tu = i2c_get_clientdata(client); 92 bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 && 93 tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL; 94 int ret = 0; 95 96 switch (event) { 97 case I2C_SLAVE_WRITE_RECEIVED: 98 if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) 99 return -EBUSY; 100 101 if (tu->reg_idx < TU_NUM_REGS) 102 tu->regs[tu->reg_idx] = *val; 103 else 104 ret = -EMSGSIZE; 105 106 if (tu->reg_idx <= TU_NUM_REGS) 107 tu->reg_idx++; 108 109 /* TU_REG_CMD always written at this point */ 110 if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS) 111 ret = -EINVAL; 112 113 break; 114 115 case I2C_SLAVE_STOP: 116 if (tu->reg_idx == TU_NUM_REGS) { 117 set_bit(TU_FLAG_IN_PROCESS, &tu->flags); 118 queue_delayed_work(system_long_wq, &tu->worker, 119 msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY])); 120 } 121 122 /* 123 * Reset reg_idx to avoid that work gets queued again in case of 124 * STOP after a following read message. But do not clear TU regs 125 * here because we still need them in the workqueue! 126 */ 127 tu->reg_idx = 0; 128 break; 129 130 case I2C_SLAVE_WRITE_REQUESTED: 131 if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) 132 return -EBUSY; 133 134 memset(tu->regs, 0, TU_NUM_REGS); 135 tu->reg_idx = 0; 136 break; 137 138 case I2C_SLAVE_READ_PROCESSED: 139 if (is_proc_call && tu->regs[TU_REG_DATAH]) 140 tu->regs[TU_REG_DATAH]--; 141 fallthrough; 142 143 case I2C_SLAVE_READ_REQUESTED: 144 *val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION; 145 break; 146 } 147 148 return ret; 149 } 150 151 static int i2c_slave_testunit_probe(struct i2c_client *client) 152 { 153 struct testunit_data *tu; 154 155 tu = devm_kzalloc(&client->dev, sizeof(struct testunit_data), GFP_KERNEL); 156 if (!tu) 157 return -ENOMEM; 158 159 tu->client = client; 160 i2c_set_clientdata(client, tu); 161 INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work); 162 163 return i2c_slave_register(client, i2c_slave_testunit_slave_cb); 164 }; 165 166 static void i2c_slave_testunit_remove(struct i2c_client *client) 167 { 168 struct testunit_data *tu = i2c_get_clientdata(client); 169 170 cancel_delayed_work_sync(&tu->worker); 171 i2c_slave_unregister(client); 172 } 173 174 static const struct i2c_device_id i2c_slave_testunit_id[] = { 175 { "slave-testunit", 0 }, 176 { } 177 }; 178 MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id); 179 180 static struct i2c_driver i2c_slave_testunit_driver = { 181 .driver = { 182 .name = "i2c-slave-testunit", 183 }, 184 .probe = i2c_slave_testunit_probe, 185 .remove = i2c_slave_testunit_remove, 186 .id_table = i2c_slave_testunit_id, 187 }; 188 module_i2c_driver(i2c_slave_testunit_driver); 189 190 MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); 191 MODULE_DESCRIPTION("I2C slave mode test unit"); 192 MODULE_LICENSE("GPL v2"); 193