root/drivers/i2c/busses/i2c-taos-evm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. taos_instantiate_device
  2. taos_smbus_xfer
  3. taos_smbus_func
  4. taos_interrupt
  5. taos_adapter_name
  6. taos_connect
  7. taos_disconnect

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Driver for the TAOS evaluation modules
   4  * These devices include an I2C master which can be controlled over the
   5  * serial port.
   6  *
   7  * Copyright (C) 2007 Jean Delvare <jdelvare@suse.de>
   8  */
   9 
  10 #include <linux/delay.h>
  11 #include <linux/module.h>
  12 #include <linux/slab.h>
  13 #include <linux/interrupt.h>
  14 #include <linux/input.h>
  15 #include <linux/serio.h>
  16 #include <linux/init.h>
  17 #include <linux/i2c.h>
  18 
  19 #define TAOS_BUFFER_SIZE        63
  20 
  21 #define TAOS_STATE_INIT         0
  22 #define TAOS_STATE_IDLE         1
  23 #define TAOS_STATE_EOFF         2
  24 #define TAOS_STATE_RECV         3
  25 
  26 #define TAOS_CMD_RESET          0x12
  27 #define TAOS_CMD_ECHO_ON        '+'
  28 #define TAOS_CMD_ECHO_OFF       '-'
  29 
  30 static DECLARE_WAIT_QUEUE_HEAD(wq);
  31 
  32 struct taos_data {
  33         struct i2c_adapter adapter;
  34         struct i2c_client *client;
  35         int state;
  36         u8 addr;                /* last used address */
  37         unsigned char buffer[TAOS_BUFFER_SIZE];
  38         unsigned int pos;       /* position inside the buffer */
  39 };
  40 
  41 /* TAOS TSL2550 EVM */
  42 static const struct i2c_board_info tsl2550_info = {
  43         I2C_BOARD_INFO("tsl2550", 0x39),
  44 };
  45 
  46 /* Instantiate i2c devices based on the adapter name */
  47 static struct i2c_client *taos_instantiate_device(struct i2c_adapter *adapter)
  48 {
  49         if (!strncmp(adapter->name, "TAOS TSL2550 EVM", 16)) {
  50                 dev_info(&adapter->dev, "Instantiating device %s at 0x%02x\n",
  51                         tsl2550_info.type, tsl2550_info.addr);
  52                 return i2c_new_device(adapter, &tsl2550_info);
  53         }
  54 
  55         return NULL;
  56 }
  57 
  58 static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
  59                            unsigned short flags, char read_write, u8 command,
  60                            int size, union i2c_smbus_data *data)
  61 {
  62         struct serio *serio = adapter->algo_data;
  63         struct taos_data *taos = serio_get_drvdata(serio);
  64         char *p;
  65 
  66         /* Encode our transaction. "@" is for the device address, "$" for the
  67            SMBus command and "#" for the data. */
  68         p = taos->buffer;
  69 
  70         /* The device remembers the last used address, no need to send it
  71            again if it's the same */
  72         if (addr != taos->addr)
  73                 p += sprintf(p, "@%02X", addr);
  74 
  75         switch (size) {
  76         case I2C_SMBUS_BYTE:
  77                 if (read_write == I2C_SMBUS_WRITE)
  78                         sprintf(p, "$#%02X", command);
  79                 else
  80                         sprintf(p, "$");
  81                 break;
  82         case I2C_SMBUS_BYTE_DATA:
  83                 if (read_write == I2C_SMBUS_WRITE)
  84                         sprintf(p, "$%02X#%02X", command, data->byte);
  85                 else
  86                         sprintf(p, "$%02X", command);
  87                 break;
  88         default:
  89                 dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);
  90                 return -EOPNOTSUPP;
  91         }
  92 
  93         /* Send the transaction to the TAOS EVM */
  94         dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer);
  95         for (p = taos->buffer; *p; p++)
  96                 serio_write(serio, *p);
  97 
  98         taos->addr = addr;
  99 
 100         /* Start the transaction and read the answer */
 101         taos->pos = 0;
 102         taos->state = TAOS_STATE_RECV;
 103         serio_write(serio, read_write == I2C_SMBUS_WRITE ? '>' : '<');
 104         wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
 105                                          msecs_to_jiffies(150));
 106         if (taos->state != TAOS_STATE_IDLE
 107          || taos->pos != 5) {
 108                 dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n",
 109                         taos->pos);
 110                 return -EIO;
 111         }
 112         dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer);
 113 
 114         /* Interpret the returned string */
 115         p = taos->buffer + 1;
 116         p[3] = '\0';
 117         if (!strcmp(p, "NAK"))
 118                 return -ENODEV;
 119 
 120         if (read_write == I2C_SMBUS_WRITE) {
 121                 if (!strcmp(p, "ACK"))
 122                         return 0;
 123         } else {
 124                 if (p[0] == 'x') {
 125                         /*
 126                          * Voluntarily dropping error code of kstrtou8 since all
 127                          * error code that it could return are invalid according
 128                          * to Documentation/i2c/fault-codes.rst.
 129                          */
 130                         if (kstrtou8(p + 1, 16, &data->byte))
 131                                 return -EPROTO;
 132                         return 0;
 133                 }
 134         }
 135 
 136         return -EIO;
 137 }
 138 
 139 static u32 taos_smbus_func(struct i2c_adapter *adapter)
 140 {
 141         return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA;
 142 }
 143 
 144 static const struct i2c_algorithm taos_algorithm = {
 145         .smbus_xfer     = taos_smbus_xfer,
 146         .functionality  = taos_smbus_func,
 147 };
 148 
 149 static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data,
 150                                   unsigned int flags)
 151 {
 152         struct taos_data *taos = serio_get_drvdata(serio);
 153 
 154         switch (taos->state) {
 155         case TAOS_STATE_INIT:
 156                 taos->buffer[taos->pos++] = data;
 157                 if (data == ':'
 158                  || taos->pos == TAOS_BUFFER_SIZE - 1) {
 159                         taos->buffer[taos->pos] = '\0';
 160                         taos->state = TAOS_STATE_IDLE;
 161                         wake_up_interruptible(&wq);
 162                 }
 163                 break;
 164         case TAOS_STATE_EOFF:
 165                 taos->state = TAOS_STATE_IDLE;
 166                 wake_up_interruptible(&wq);
 167                 break;
 168         case TAOS_STATE_RECV:
 169                 taos->buffer[taos->pos++] = data;
 170                 if (data == ']') {
 171                         taos->buffer[taos->pos] = '\0';
 172                         taos->state = TAOS_STATE_IDLE;
 173                         wake_up_interruptible(&wq);
 174                 }
 175                 break;
 176         }
 177 
 178         return IRQ_HANDLED;
 179 }
 180 
 181 /* Extract the adapter name from the buffer received after reset.
 182    The buffer is modified and a pointer inside the buffer is returned. */
 183 static char *taos_adapter_name(char *buffer)
 184 {
 185         char *start, *end;
 186 
 187         start = strstr(buffer, "TAOS ");
 188         if (!start)
 189                 return NULL;
 190 
 191         end = strchr(start, '\r');
 192         if (!end)
 193                 return NULL;
 194         *end = '\0';
 195 
 196         return start;
 197 }
 198 
 199 static int taos_connect(struct serio *serio, struct serio_driver *drv)
 200 {
 201         struct taos_data *taos;
 202         struct i2c_adapter *adapter;
 203         char *name;
 204         int err;
 205 
 206         taos = kzalloc(sizeof(struct taos_data), GFP_KERNEL);
 207         if (!taos) {
 208                 err = -ENOMEM;
 209                 goto exit;
 210         }
 211         taos->state = TAOS_STATE_INIT;
 212         serio_set_drvdata(serio, taos);
 213 
 214         err = serio_open(serio, drv);
 215         if (err)
 216                 goto exit_kfree;
 217 
 218         adapter = &taos->adapter;
 219         adapter->owner = THIS_MODULE;
 220         adapter->algo = &taos_algorithm;
 221         adapter->algo_data = serio;
 222         adapter->dev.parent = &serio->dev;
 223 
 224         /* Reset the TAOS evaluation module to identify it */
 225         serio_write(serio, TAOS_CMD_RESET);
 226         wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
 227                                          msecs_to_jiffies(2000));
 228 
 229         if (taos->state != TAOS_STATE_IDLE) {
 230                 err = -ENODEV;
 231                 dev_err(&serio->dev, "TAOS EVM reset failed (state=%d, "
 232                         "pos=%d)\n", taos->state, taos->pos);
 233                 goto exit_close;
 234         }
 235 
 236         name = taos_adapter_name(taos->buffer);
 237         if (!name) {
 238                 err = -ENODEV;
 239                 dev_err(&serio->dev, "TAOS EVM identification failed\n");
 240                 goto exit_close;
 241         }
 242         strlcpy(adapter->name, name, sizeof(adapter->name));
 243 
 244         /* Turn echo off for better performance */
 245         taos->state = TAOS_STATE_EOFF;
 246         serio_write(serio, TAOS_CMD_ECHO_OFF);
 247 
 248         wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
 249                                          msecs_to_jiffies(250));
 250         if (taos->state != TAOS_STATE_IDLE) {
 251                 err = -ENODEV;
 252                 dev_err(&serio->dev, "TAOS EVM echo off failed "
 253                         "(state=%d)\n", taos->state);
 254                 goto exit_close;
 255         }
 256 
 257         err = i2c_add_adapter(adapter);
 258         if (err)
 259                 goto exit_close;
 260         dev_info(&serio->dev, "Connected to TAOS EVM\n");
 261 
 262         taos->client = taos_instantiate_device(adapter);
 263         return 0;
 264 
 265  exit_close:
 266         serio_close(serio);
 267  exit_kfree:
 268         kfree(taos);
 269  exit:
 270         return err;
 271 }
 272 
 273 static void taos_disconnect(struct serio *serio)
 274 {
 275         struct taos_data *taos = serio_get_drvdata(serio);
 276 
 277         i2c_unregister_device(taos->client);
 278         i2c_del_adapter(&taos->adapter);
 279         serio_close(serio);
 280         kfree(taos);
 281 
 282         dev_info(&serio->dev, "Disconnected from TAOS EVM\n");
 283 }
 284 
 285 static const struct serio_device_id taos_serio_ids[] = {
 286         {
 287                 .type   = SERIO_RS232,
 288                 .proto  = SERIO_TAOSEVM,
 289                 .id     = SERIO_ANY,
 290                 .extra  = SERIO_ANY,
 291         },
 292         { 0 }
 293 };
 294 MODULE_DEVICE_TABLE(serio, taos_serio_ids);
 295 
 296 static struct serio_driver taos_drv = {
 297         .driver         = {
 298                 .name   = "taos-evm",
 299         },
 300         .description    = "TAOS evaluation module driver",
 301         .id_table       = taos_serio_ids,
 302         .connect        = taos_connect,
 303         .disconnect     = taos_disconnect,
 304         .interrupt      = taos_interrupt,
 305 };
 306 
 307 module_serio_driver(taos_drv);
 308 
 309 MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
 310 MODULE_DESCRIPTION("TAOS evaluation module driver");
 311 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */