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