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