Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * I2C slave mode testunit
0004  *
0005  * Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
0006  * Copyright (C) 2020 by Renesas Electronics Corporation
0007  */
0008 
0009 #include <linux/bitops.h>
0010 #include <linux/i2c.h>
0011 #include <linux/init.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/slab.h>
0015 #include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
0016 
0017 #define TU_CUR_VERSION 0x01
0018 
0019 enum testunit_cmds {
0020     TU_CMD_READ_BYTES = 1,  /* save 0 for ABORT, RESET or similar */
0021     TU_CMD_HOST_NOTIFY,
0022     TU_CMD_SMBUS_BLOCK_PROC_CALL,
0023     TU_NUM_CMDS
0024 };
0025 
0026 enum testunit_regs {
0027     TU_REG_CMD,
0028     TU_REG_DATAL,
0029     TU_REG_DATAH,
0030     TU_REG_DELAY,
0031     TU_NUM_REGS
0032 };
0033 
0034 enum testunit_flags {
0035     TU_FLAG_IN_PROCESS,
0036 };
0037 
0038 struct testunit_data {
0039     unsigned long flags;
0040     u8 regs[TU_NUM_REGS];
0041     u8 reg_idx;
0042     struct i2c_client *client;
0043     struct delayed_work worker;
0044 };
0045 
0046 static void i2c_slave_testunit_work(struct work_struct *work)
0047 {
0048     struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
0049     struct i2c_msg msg;
0050     u8 msgbuf[256];
0051     int ret = 0;
0052 
0053     msg.addr = I2C_CLIENT_END;
0054     msg.buf = msgbuf;
0055 
0056     switch (tu->regs[TU_REG_CMD]) {
0057     case TU_CMD_READ_BYTES:
0058         msg.addr = tu->regs[TU_REG_DATAL];
0059         msg.flags = I2C_M_RD;
0060         msg.len = tu->regs[TU_REG_DATAH];
0061         break;
0062 
0063     case TU_CMD_HOST_NOTIFY:
0064         msg.addr = 0x08;
0065         msg.flags = 0;
0066         msg.len = 3;
0067         msgbuf[0] = tu->client->addr;
0068         msgbuf[1] = tu->regs[TU_REG_DATAL];
0069         msgbuf[2] = tu->regs[TU_REG_DATAH];
0070         break;
0071 
0072     default:
0073         break;
0074     }
0075 
0076     if (msg.addr != I2C_CLIENT_END) {
0077         ret = i2c_transfer(tu->client->adapter, &msg, 1);
0078         /* convert '0 msgs transferred' to errno */
0079         ret = (ret == 0) ? -EIO : ret;
0080     }
0081 
0082     if (ret < 0)
0083         dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret);
0084 
0085     clear_bit(TU_FLAG_IN_PROCESS, &tu->flags);
0086 }
0087 
0088 static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
0089                      enum i2c_slave_event event, u8 *val)
0090 {
0091     struct testunit_data *tu = i2c_get_clientdata(client);
0092     bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 &&
0093                 tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
0094     int ret = 0;
0095 
0096     switch (event) {
0097     case I2C_SLAVE_WRITE_RECEIVED:
0098         if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
0099             return -EBUSY;
0100 
0101         if (tu->reg_idx < TU_NUM_REGS)
0102             tu->regs[tu->reg_idx] = *val;
0103         else
0104             ret = -EMSGSIZE;
0105 
0106         if (tu->reg_idx <= TU_NUM_REGS)
0107             tu->reg_idx++;
0108 
0109         /* TU_REG_CMD always written at this point */
0110         if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
0111             ret = -EINVAL;
0112 
0113         break;
0114 
0115     case I2C_SLAVE_STOP:
0116         if (tu->reg_idx == TU_NUM_REGS) {
0117             set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
0118             queue_delayed_work(system_long_wq, &tu->worker,
0119                        msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
0120         }
0121         fallthrough;
0122 
0123     case I2C_SLAVE_WRITE_REQUESTED:
0124         memset(tu->regs, 0, TU_NUM_REGS);
0125         tu->reg_idx = 0;
0126         break;
0127 
0128     case I2C_SLAVE_READ_PROCESSED:
0129         if (is_proc_call && tu->regs[TU_REG_DATAH])
0130             tu->regs[TU_REG_DATAH]--;
0131         fallthrough;
0132 
0133     case I2C_SLAVE_READ_REQUESTED:
0134         *val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION;
0135         break;
0136     }
0137 
0138     return ret;
0139 }
0140 
0141 static int i2c_slave_testunit_probe(struct i2c_client *client)
0142 {
0143     struct testunit_data *tu;
0144 
0145     tu = devm_kzalloc(&client->dev, sizeof(struct testunit_data), GFP_KERNEL);
0146     if (!tu)
0147         return -ENOMEM;
0148 
0149     tu->client = client;
0150     i2c_set_clientdata(client, tu);
0151     INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
0152 
0153     return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
0154 };
0155 
0156 static int i2c_slave_testunit_remove(struct i2c_client *client)
0157 {
0158     struct testunit_data *tu = i2c_get_clientdata(client);
0159 
0160     cancel_delayed_work_sync(&tu->worker);
0161     i2c_slave_unregister(client);
0162     return 0;
0163 }
0164 
0165 static const struct i2c_device_id i2c_slave_testunit_id[] = {
0166     { "slave-testunit", 0 },
0167     { }
0168 };
0169 MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id);
0170 
0171 static struct i2c_driver i2c_slave_testunit_driver = {
0172     .driver = {
0173         .name = "i2c-slave-testunit",
0174     },
0175     .probe_new = i2c_slave_testunit_probe,
0176     .remove = i2c_slave_testunit_remove,
0177     .id_table = i2c_slave_testunit_id,
0178 };
0179 module_i2c_driver(i2c_slave_testunit_driver);
0180 
0181 MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
0182 MODULE_DESCRIPTION("I2C slave mode test unit");
0183 MODULE_LICENSE("GPL v2");