Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for the TAOS evaluation modules
0004  * These devices include an I2C master which can be controlled over the
0005  * serial port.
0006  *
0007  * Copyright (C) 2007 Jean Delvare <jdelvare@suse.de>
0008  */
0009 
0010 #include <linux/delay.h>
0011 #include <linux/module.h>
0012 #include <linux/slab.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/input.h>
0015 #include <linux/serio.h>
0016 #include <linux/init.h>
0017 #include <linux/i2c.h>
0018 
0019 #define TAOS_BUFFER_SIZE    63
0020 
0021 #define TAOS_STATE_INIT     0
0022 #define TAOS_STATE_IDLE     1
0023 #define TAOS_STATE_EOFF     2
0024 #define TAOS_STATE_RECV     3
0025 
0026 #define TAOS_CMD_RESET      0x12
0027 #define TAOS_CMD_ECHO_ON    '+'
0028 #define TAOS_CMD_ECHO_OFF   '-'
0029 
0030 static DECLARE_WAIT_QUEUE_HEAD(wq);
0031 
0032 struct taos_data {
0033     struct i2c_adapter adapter;
0034     struct i2c_client *client;
0035     int state;
0036     u8 addr;        /* last used address */
0037     unsigned char buffer[TAOS_BUFFER_SIZE];
0038     unsigned int pos;   /* position inside the buffer */
0039 };
0040 
0041 /* TAOS TSL2550 EVM */
0042 static const struct i2c_board_info tsl2550_info = {
0043     I2C_BOARD_INFO("tsl2550", 0x39),
0044 };
0045 
0046 /* Instantiate i2c devices based on the adapter name */
0047 static struct i2c_client *taos_instantiate_device(struct i2c_adapter *adapter)
0048 {
0049     if (!strncmp(adapter->name, "TAOS TSL2550 EVM", 16)) {
0050         dev_info(&adapter->dev, "Instantiating device %s at 0x%02x\n",
0051             tsl2550_info.type, tsl2550_info.addr);
0052         return i2c_new_client_device(adapter, &tsl2550_info);
0053     }
0054 
0055     return ERR_PTR(-ENODEV);
0056 }
0057 
0058 static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
0059                unsigned short flags, char read_write, u8 command,
0060                int size, union i2c_smbus_data *data)
0061 {
0062     struct serio *serio = adapter->algo_data;
0063     struct taos_data *taos = serio_get_drvdata(serio);
0064     char *p;
0065 
0066     /* Encode our transaction. "@" is for the device address, "$" for the
0067        SMBus command and "#" for the data. */
0068     p = taos->buffer;
0069 
0070     /* The device remembers the last used address, no need to send it
0071        again if it's the same */
0072     if (addr != taos->addr)
0073         p += sprintf(p, "@%02X", addr);
0074 
0075     switch (size) {
0076     case I2C_SMBUS_BYTE:
0077         if (read_write == I2C_SMBUS_WRITE)
0078             sprintf(p, "$#%02X", command);
0079         else
0080             sprintf(p, "$");
0081         break;
0082     case I2C_SMBUS_BYTE_DATA:
0083         if (read_write == I2C_SMBUS_WRITE)
0084             sprintf(p, "$%02X#%02X", command, data->byte);
0085         else
0086             sprintf(p, "$%02X", command);
0087         break;
0088     default:
0089         dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);
0090         return -EOPNOTSUPP;
0091     }
0092 
0093     /* Send the transaction to the TAOS EVM */
0094     dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer);
0095     for (p = taos->buffer; *p; p++)
0096         serio_write(serio, *p);
0097 
0098     taos->addr = addr;
0099 
0100     /* Start the transaction and read the answer */
0101     taos->pos = 0;
0102     taos->state = TAOS_STATE_RECV;
0103     serio_write(serio, read_write == I2C_SMBUS_WRITE ? '>' : '<');
0104     wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
0105                      msecs_to_jiffies(150));
0106     if (taos->state != TAOS_STATE_IDLE
0107      || taos->pos != 5) {
0108         dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n",
0109             taos->pos);
0110         return -EIO;
0111     }
0112     dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer);
0113 
0114     /* Interpret the returned string */
0115     p = taos->buffer + 1;
0116     p[3] = '\0';
0117     if (!strcmp(p, "NAK"))
0118         return -ENODEV;
0119 
0120     if (read_write == I2C_SMBUS_WRITE) {
0121         if (!strcmp(p, "ACK"))
0122             return 0;
0123     } else {
0124         if (p[0] == 'x') {
0125             /*
0126              * Voluntarily dropping error code of kstrtou8 since all
0127              * error code that it could return are invalid according
0128              * to Documentation/i2c/fault-codes.rst.
0129              */
0130             if (kstrtou8(p + 1, 16, &data->byte))
0131                 return -EPROTO;
0132             return 0;
0133         }
0134     }
0135 
0136     return -EIO;
0137 }
0138 
0139 static u32 taos_smbus_func(struct i2c_adapter *adapter)
0140 {
0141     return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA;
0142 }
0143 
0144 static const struct i2c_algorithm taos_algorithm = {
0145     .smbus_xfer = taos_smbus_xfer,
0146     .functionality  = taos_smbus_func,
0147 };
0148 
0149 static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data,
0150                   unsigned int flags)
0151 {
0152     struct taos_data *taos = serio_get_drvdata(serio);
0153 
0154     switch (taos->state) {
0155     case TAOS_STATE_INIT:
0156         taos->buffer[taos->pos++] = data;
0157         if (data == ':'
0158          || taos->pos == TAOS_BUFFER_SIZE - 1) {
0159             taos->buffer[taos->pos] = '\0';
0160             taos->state = TAOS_STATE_IDLE;
0161             wake_up_interruptible(&wq);
0162         }
0163         break;
0164     case TAOS_STATE_EOFF:
0165         taos->state = TAOS_STATE_IDLE;
0166         wake_up_interruptible(&wq);
0167         break;
0168     case TAOS_STATE_RECV:
0169         taos->buffer[taos->pos++] = data;
0170         if (data == ']') {
0171             taos->buffer[taos->pos] = '\0';
0172             taos->state = TAOS_STATE_IDLE;
0173             wake_up_interruptible(&wq);
0174         }
0175         break;
0176     }
0177 
0178     return IRQ_HANDLED;
0179 }
0180 
0181 /* Extract the adapter name from the buffer received after reset.
0182    The buffer is modified and a pointer inside the buffer is returned. */
0183 static char *taos_adapter_name(char *buffer)
0184 {
0185     char *start, *end;
0186 
0187     start = strstr(buffer, "TAOS ");
0188     if (!start)
0189         return NULL;
0190 
0191     end = strchr(start, '\r');
0192     if (!end)
0193         return NULL;
0194     *end = '\0';
0195 
0196     return start;
0197 }
0198 
0199 static int taos_connect(struct serio *serio, struct serio_driver *drv)
0200 {
0201     struct taos_data *taos;
0202     struct i2c_adapter *adapter;
0203     char *name;
0204     int err;
0205 
0206     taos = kzalloc(sizeof(struct taos_data), GFP_KERNEL);
0207     if (!taos) {
0208         err = -ENOMEM;
0209         goto exit;
0210     }
0211     taos->state = TAOS_STATE_INIT;
0212     serio_set_drvdata(serio, taos);
0213 
0214     err = serio_open(serio, drv);
0215     if (err)
0216         goto exit_kfree;
0217 
0218     adapter = &taos->adapter;
0219     adapter->owner = THIS_MODULE;
0220     adapter->algo = &taos_algorithm;
0221     adapter->algo_data = serio;
0222     adapter->dev.parent = &serio->dev;
0223 
0224     /* Reset the TAOS evaluation module to identify it */
0225     serio_write(serio, TAOS_CMD_RESET);
0226     wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
0227                      msecs_to_jiffies(2000));
0228 
0229     if (taos->state != TAOS_STATE_IDLE) {
0230         err = -ENODEV;
0231         dev_err(&serio->dev, "TAOS EVM reset failed (state=%d, "
0232             "pos=%d)\n", taos->state, taos->pos);
0233         goto exit_close;
0234     }
0235 
0236     name = taos_adapter_name(taos->buffer);
0237     if (!name) {
0238         err = -ENODEV;
0239         dev_err(&serio->dev, "TAOS EVM identification failed\n");
0240         goto exit_close;
0241     }
0242     strscpy(adapter->name, name, sizeof(adapter->name));
0243 
0244     /* Turn echo off for better performance */
0245     taos->state = TAOS_STATE_EOFF;
0246     serio_write(serio, TAOS_CMD_ECHO_OFF);
0247 
0248     wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
0249                      msecs_to_jiffies(250));
0250     if (taos->state != TAOS_STATE_IDLE) {
0251         err = -ENODEV;
0252         dev_err(&serio->dev, "TAOS EVM echo off failed "
0253             "(state=%d)\n", taos->state);
0254         goto exit_close;
0255     }
0256 
0257     err = i2c_add_adapter(adapter);
0258     if (err)
0259         goto exit_close;
0260     dev_info(&serio->dev, "Connected to TAOS EVM\n");
0261 
0262     taos->client = taos_instantiate_device(adapter);
0263     return 0;
0264 
0265  exit_close:
0266     serio_close(serio);
0267  exit_kfree:
0268     kfree(taos);
0269  exit:
0270     return err;
0271 }
0272 
0273 static void taos_disconnect(struct serio *serio)
0274 {
0275     struct taos_data *taos = serio_get_drvdata(serio);
0276 
0277     i2c_unregister_device(taos->client);
0278     i2c_del_adapter(&taos->adapter);
0279     serio_close(serio);
0280     kfree(taos);
0281 
0282     dev_info(&serio->dev, "Disconnected from TAOS EVM\n");
0283 }
0284 
0285 static const struct serio_device_id taos_serio_ids[] = {
0286     {
0287         .type   = SERIO_RS232,
0288         .proto  = SERIO_TAOSEVM,
0289         .id = SERIO_ANY,
0290         .extra  = SERIO_ANY,
0291     },
0292     { 0 }
0293 };
0294 MODULE_DEVICE_TABLE(serio, taos_serio_ids);
0295 
0296 static struct serio_driver taos_drv = {
0297     .driver     = {
0298         .name   = "taos-evm",
0299     },
0300     .description    = "TAOS evaluation module driver",
0301     .id_table   = taos_serio_ids,
0302     .connect    = taos_connect,
0303     .disconnect = taos_disconnect,
0304     .interrupt  = taos_interrupt,
0305 };
0306 
0307 module_serio_driver(taos_drv);
0308 
0309 MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
0310 MODULE_DESCRIPTION("TAOS evaluation module driver");
0311 MODULE_LICENSE("GPL");