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 38static DECLARE_WAIT_QUEUE_HEAD(wq); 39 40struct 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 */ 50static struct i2c_board_info tsl2550_info = { 51 I2C_BOARD_INFO("tsl2550", 0x39), 52}; 53 54/* Instantiate i2c devices based on the adapter name */ 55static 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 66static 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 data->byte = simple_strtol(p + 1, NULL, 16); 134 return 0; 135 } 136 } 137 138 return -EIO; 139} 140 141static u32 taos_smbus_func(struct i2c_adapter *adapter) 142{ 143 return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA; 144} 145 146static const struct i2c_algorithm taos_algorithm = { 147 .smbus_xfer = taos_smbus_xfer, 148 .functionality = taos_smbus_func, 149}; 150 151static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data, 152 unsigned int flags) 153{ 154 struct taos_data *taos = serio_get_drvdata(serio); 155 156 switch (taos->state) { 157 case TAOS_STATE_INIT: 158 taos->buffer[taos->pos++] = data; 159 if (data == ':' 160 || taos->pos == TAOS_BUFFER_SIZE - 1) { 161 taos->buffer[taos->pos] = '\0'; 162 taos->state = TAOS_STATE_IDLE; 163 wake_up_interruptible(&wq); 164 } 165 break; 166 case TAOS_STATE_EOFF: 167 taos->state = TAOS_STATE_IDLE; 168 wake_up_interruptible(&wq); 169 break; 170 case TAOS_STATE_RECV: 171 taos->buffer[taos->pos++] = data; 172 if (data == ']') { 173 taos->buffer[taos->pos] = '\0'; 174 taos->state = TAOS_STATE_IDLE; 175 wake_up_interruptible(&wq); 176 } 177 break; 178 } 179 180 return IRQ_HANDLED; 181} 182 183/* Extract the adapter name from the buffer received after reset. 184 The buffer is modified and a pointer inside the buffer is returned. */ 185static char *taos_adapter_name(char *buffer) 186{ 187 char *start, *end; 188 189 start = strstr(buffer, "TAOS "); 190 if (!start) 191 return NULL; 192 193 end = strchr(start, '\r'); 194 if (!end) 195 return NULL; 196 *end = '\0'; 197 198 return start; 199} 200 201static int taos_connect(struct serio *serio, struct serio_driver *drv) 202{ 203 struct taos_data *taos; 204 struct i2c_adapter *adapter; 205 char *name; 206 int err; 207 208 taos = kzalloc(sizeof(struct taos_data), GFP_KERNEL); 209 if (!taos) { 210 err = -ENOMEM; 211 goto exit; 212 } 213 taos->state = TAOS_STATE_INIT; 214 serio_set_drvdata(serio, taos); 215 216 err = serio_open(serio, drv); 217 if (err) 218 goto exit_kfree; 219 220 adapter = &taos->adapter; 221 adapter->owner = THIS_MODULE; 222 adapter->algo = &taos_algorithm; 223 adapter->algo_data = serio; 224 adapter->dev.parent = &serio->dev; 225 226 /* Reset the TAOS evaluation module to identify it */ 227 serio_write(serio, TAOS_CMD_RESET); 228 wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, 229 msecs_to_jiffies(2000)); 230 231 if (taos->state != TAOS_STATE_IDLE) { 232 err = -ENODEV; 233 dev_err(&serio->dev, "TAOS EVM reset failed (state=%d, " 234 "pos=%d)\n", taos->state, taos->pos); 235 goto exit_close; 236 } 237 238 name = taos_adapter_name(taos->buffer); 239 if (!name) { 240 err = -ENODEV; 241 dev_err(&serio->dev, "TAOS EVM identification failed\n"); 242 goto exit_close; 243 } 244 strlcpy(adapter->name, name, sizeof(adapter->name)); 245 246 /* Turn echo off for better performance */ 247 taos->state = TAOS_STATE_EOFF; 248 serio_write(serio, TAOS_CMD_ECHO_OFF); 249 250 wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, 251 msecs_to_jiffies(250)); 252 if (taos->state != TAOS_STATE_IDLE) { 253 err = -ENODEV; 254 dev_err(&serio->dev, "TAOS EVM echo off failed " 255 "(state=%d)\n", taos->state); 256 goto exit_close; 257 } 258 259 err = i2c_add_adapter(adapter); 260 if (err) 261 goto exit_close; 262 dev_info(&serio->dev, "Connected to TAOS EVM\n"); 263 264 taos->client = taos_instantiate_device(adapter); 265 return 0; 266 267 exit_close: 268 serio_close(serio); 269 exit_kfree: 270 kfree(taos); 271 exit: 272 return err; 273} 274 275static void taos_disconnect(struct serio *serio) 276{ 277 struct taos_data *taos = serio_get_drvdata(serio); 278 279 if (taos->client) 280 i2c_unregister_device(taos->client); 281 i2c_del_adapter(&taos->adapter); 282 serio_close(serio); 283 kfree(taos); 284 285 dev_info(&serio->dev, "Disconnected from TAOS EVM\n"); 286} 287 288static struct serio_device_id taos_serio_ids[] = { 289 { 290 .type = SERIO_RS232, 291 .proto = SERIO_TAOSEVM, 292 .id = SERIO_ANY, 293 .extra = SERIO_ANY, 294 }, 295 { 0 } 296}; 297MODULE_DEVICE_TABLE(serio, taos_serio_ids); 298 299static struct serio_driver taos_drv = { 300 .driver = { 301 .name = "taos-evm", 302 }, 303 .description = "TAOS evaluation module driver", 304 .id_table = taos_serio_ids, 305 .connect = taos_connect, 306 .disconnect = taos_disconnect, 307 .interrupt = taos_interrupt, 308}; 309 310module_serio_driver(taos_drv); 311 312MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 313MODULE_DESCRIPTION("TAOS evaluation module driver"); 314MODULE_LICENSE("GPL"); 315