summaryrefslogtreecommitdiff
path: root/firmware/drivers/synaptics-mep.c
diff options
context:
space:
mode:
authorMark Arigo <markarigo@gmail.com>2008-12-17 04:38:53 +0000
committerMark Arigo <markarigo@gmail.com>2008-12-17 04:38:53 +0000
commitf2b66918e635dbd4860bf3af4303d2e86d182983 (patch)
tree492794a18c0036b8fca261501a71e8767245e131 /firmware/drivers/synaptics-mep.c
parenta742c3cd56d802f72567167e6e5482308744de3e (diff)
downloadrockbox-f2b66918e635dbd4860bf3af4303d2e86d182983.tar.gz
rockbox-f2b66918e635dbd4860bf3af4303d2e86d182983.zip
Move the Synaptics touchpad driver for the m:robe 100 to a common place. It will (soon) be used for the Philips HDD1630.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19465 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/synaptics-mep.c')
-rw-r--r--firmware/drivers/synaptics-mep.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/firmware/drivers/synaptics-mep.c b/firmware/drivers/synaptics-mep.c
new file mode 100644
index 0000000000..44b186034f
--- /dev/null
+++ b/firmware/drivers/synaptics-mep.c
@@ -0,0 +1,482 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 by Mark Arigo
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23#include "config.h"
24#include "cpu.h"
25#include "system.h"
26#include "kernel.h"
27#include "system.h"
28
29#define LOGF_ENABLE
30#include "logf.h"
31
32/* Driver for the Synaptics Touchpad based on the "Synaptics Modular Embedded
33 Protocol: 3-Wire Interface Specification" documentation */
34
35#define ACK (GPIOD_INPUT_VAL & 0x1)
36#define ACK_HI GPIOD_OUTPUT_VAL |= 0x1
37#define ACK_LO GPIOD_OUTPUT_VAL &= ~0x1
38
39#define CLK ((GPIOD_INPUT_VAL & 0x2) >> 1)
40#define CLK_HI GPIOD_OUTPUT_VAL |= 0x2
41#define CLK_LO GPIOD_OUTPUT_VAL &= ~0x2
42
43#define DATA ((GPIOD_INPUT_VAL & 0x4) >> 2)
44#define DATA_HI GPIOD_OUTPUT_EN |= 0x4; GPIOD_OUTPUT_VAL |= 0x4
45#define DATA_LO GPIOD_OUTPUT_EN |= 0x4; GPIOD_OUTPUT_VAL &= ~0x4
46#define DATA_CL GPIOD_OUTPUT_EN &= ~0x4
47
48#define LO 0
49#define HI 1
50
51#define READ_RETRY 8
52#define READ_ERROR -1
53
54#define MEP_HELLO_HEADER 0x19
55#define MEP_HELLO_ID 0x1
56
57#define MEP_READ 0x1
58#define MEP_WRITE 0x3
59
60static int syn_wait_clk_change(unsigned int val)
61{
62 int i;
63
64 for (i = 0; i < 10000; i++)
65 {
66 if (CLK == val)
67 return 1;
68 }
69
70 return 0;
71}
72
73static inline int syn_get_data(void)
74{
75 DATA_CL;
76 return DATA;
77}
78
79static void syn_wait_guest_flush(void)
80{
81 /* Flush receiving (flushee) state:
82 handshake until DATA goes high during P3 stage */
83 if (CLK == LO)
84 {
85 ACK_HI; /* P1 -> P2 */
86 syn_wait_clk_change(HI); /* P2 -> P3 */
87 }
88
89 while (syn_get_data() == LO)
90 {
91 ACK_HI; /* P3 -> P0 */
92 syn_wait_clk_change(LO); /* P0 -> P1 */
93 ACK_LO; /* P1 -> P2 */
94 syn_wait_clk_change(HI); /* P2 -> P3 */
95 }
96
97 /* Continue handshaking until back to P0 */
98 ACK_HI; /* P3 -> P0 */
99}
100
101static void syn_flush(void)
102{
103 int i;
104
105 logf("syn_flush...");
106
107 /* Flusher holds DATA low for at least 36 handshake cycles */
108 DATA_LO;
109
110 for (i = 0; i < 36; i++)
111 {
112 syn_wait_clk_change(LO); /* P0 -> P1 */
113 ACK_LO; /* P1 -> P2 */
114 syn_wait_clk_change(HI); /* P2 -> P3 */
115 ACK_HI; /* P3 -> P0 */
116 }
117
118 /* Raise DATA in P1 stage */
119 syn_wait_clk_change(LO); /* P0 -> P1 */
120 DATA_HI;
121
122 /* After a flush, the flushing device enters a flush-receiving (flushee)
123 state */
124 syn_wait_guest_flush();
125}
126
127static int syn_send_data(int *data, int len)
128{
129 int i, bit;
130 int parity = 0;
131
132 logf("syn_send_data...");
133
134 /* 1. Lower DATA line to issue a request-to-send to guest */
135 DATA_LO;
136
137 /* 2. Wait for guest to lower CLK */
138 syn_wait_clk_change(LO);
139
140 /* 3. Lower ACK (with DATA still low) */
141 ACK_LO;
142
143 /* 4. Wait for guest to raise CLK */
144 syn_wait_clk_change(HI);
145
146 /* 5. Send data */
147 for (i = 0; i < len; i++)
148 {
149 logf(" sending byte: %d", data[i]);
150
151 bit = 0;
152 while (bit < 8)
153 {
154 /* 5a. Drive data low if bit is 0, or high if bit is 1 */
155 if (data[i] & (1 << bit))
156 {
157 DATA_HI;
158 parity++;
159 }
160 else
161 {
162 DATA_LO;
163 }
164 bit++;
165
166 /* 5b. Invert ACK to indicate that the data bit is ready */
167 ACK_HI;
168
169 /* 5c. Wait for guest to invert CLK */
170 syn_wait_clk_change(LO);
171
172 /* Repeat for next bit */
173 if (data[i] & (1 << bit))
174 {
175 DATA_HI;
176 parity++;
177 }
178 else
179 {
180 DATA_LO;
181 }
182 bit++;
183
184 ACK_LO;
185
186 syn_wait_clk_change(HI);
187 }
188 }
189
190 /* 7. Transmission termination sequence: */
191 /* 7a. Host may put parity bit on DATA. Hosts that do not generate
192 parity should set DATA high. Parity is 1 if there's an odd
193 number of '1' bits, or 0 if there's an even number of '1' bits. */
194 parity = parity % 2;
195 if (parity)
196 {
197 DATA_HI;
198 }
199 else
200 {
201 DATA_LO;
202 }
203 logf(" send parity = %d", parity);
204
205 /* 7b. Raise ACK to indicate that the optional parity bit is ready */
206 ACK_HI;
207
208 /* 7c. Guest lowers CLK */
209 syn_wait_clk_change(LO);
210
211 /* 7d. Pull DATA high (if parity bit was 0) */
212 DATA_HI;
213
214 /* 7e. Lower ACK to indicate that the stop bit is ready */
215 ACK_LO;
216
217 /* 7f. Guest raises CLK */
218 syn_wait_clk_change(HI);
219
220 /* 7g. If DATA is low, guest is flushing this transfer. Host should
221 enter the flushee state. */
222 if (syn_get_data() == LO)
223 {
224 logf(" module flushing");
225
226 syn_wait_guest_flush();
227 return -1;
228 }
229
230 /* 7h. Host raises ACK and the link enters the idle state */
231 ACK_HI;
232
233 return len;
234}
235
236static int syn_read_data(int *data, int data_len)
237{
238 int i, len, bit, parity, tmp;
239 int *data_ptr;
240
241 logf("syn_read_data...");
242
243 /* 1. Guest drives CLK low */
244 if (CLK != LO)
245 return 0;
246
247 /* 1a. If the host is willing to receive a packet it lowers ACK */
248 ACK_LO;
249
250 /* 2. Guest may issue a request-to-send by lowering DATA. If the
251 guest decides not to transmit a packet, it may abort the
252 transmission by not lowering DATA. */
253
254 /* 3. The guest raises CLK */
255 syn_wait_clk_change(HI);
256
257 /* 4. If the guest is still driving DATA low, the transfer is commited
258 to occur. Otherwise, the transfer is aborted. In either case,
259 the host raises ACK. */
260 if (syn_get_data() == HI)
261 {
262 logf(" read abort");
263
264 ACK_HI;
265 return READ_ERROR;
266 }
267 else
268 {
269 ACK_HI;
270 }
271
272 /* 5. Read the incoming data packet */
273 i = 0;
274 len = 0;
275 parity = 0;
276 while (i <= len)
277 {
278 bit = 0;
279
280 if (i < data_len)
281 data_ptr = &data[i];
282 else
283 data_ptr = &tmp;
284
285 *data_ptr = 0;
286 while (bit < 8)
287 {
288 /* 5b. Guset inverts CLK to indicate that data is ready */
289 syn_wait_clk_change(LO);
290
291 /* 5d. Read the data bit from DATA */
292 if (syn_get_data() == HI)
293 {
294 *data_ptr |= (1 << bit);
295 parity++;
296 }
297 bit++;
298
299 /* 5e. Invert ACK to indicate that data has been read */
300 ACK_LO;
301
302 /* Repeat for next bit */
303 syn_wait_clk_change(HI);
304
305 if (syn_get_data() == HI)
306 {
307 *data_ptr |= (1 << bit);
308 parity++;
309 }
310 bit++;
311
312 ACK_HI;
313 }
314
315 /* First byte is the packet header */
316 if (i == 0)
317 {
318 /* Format control (bit 3) should be 1 */
319 if (*data_ptr & 0x8)
320 {
321 /* Packet length is bits 0:2 */
322 len = *data_ptr & 0x7;
323 logf(" packet length = %d", len);
324 }
325 else
326 {
327 logf(" invalid format ctrl bit");
328 return READ_ERROR;
329 }
330 }
331
332 i++;
333 }
334
335 /* 7. Transmission termination cycle */
336 /* 7a. The guest generates a parity bit on DATA */
337 /* 7b. The host waits for guest to lower CLK */
338 syn_wait_clk_change(LO);
339
340 /* 7c. The host verifies the parity bit is correct */
341 parity = parity % 2;
342 logf(" parity check: %d / %d", syn_get_data(), parity);
343
344 /* TODO: parity error handling */
345
346 /* 7d. The host lowers ACK */
347 ACK_LO;
348
349 /* 7e. The host waits for the guest to raise CLK indicating
350 that the stop bit is ready */
351 syn_wait_clk_change(HI);
352
353 /* 7f. The host reads DATA and verifies that it is 1 */
354 if (syn_get_data() == LO)
355 {
356 logf(" framing error");
357
358 ACK_HI;
359 return READ_ERROR;
360 }
361
362 ACK_HI;
363
364 return len;
365}
366
367int syn_read_device(int *data, int len)
368{
369 int i;
370 int ret = READ_ERROR;
371
372 for (i = 0; i < READ_RETRY; i++)
373 {
374 if (syn_wait_clk_change(LO))
375 {
376 /* module is sending data */
377 ret = syn_read_data(data, len);
378 if (ret != READ_ERROR)
379 return ret;
380
381 syn_flush();
382 }
383 else
384 {
385 /* module is idle */
386 return 0;
387 }
388 }
389
390 return ret;
391}
392
393static int syn_reset(void)
394{
395 int val, id;
396 int data[2];
397
398 logf("syn_reset...");
399
400 /* reset module 0 */
401 val = (0 << 4) | (1 << 3) | 0;
402 syn_send_data(&val, 1);
403
404 val = syn_read_device(data, 2);
405 if (val == 1)
406 {
407 val = data[0] & 0xff; /* packet header */
408 id = (data[1] >> 4) & 0xf; /* packet id */
409 if ((val == MEP_HELLO_HEADER) && (id == MEP_HELLO_ID))
410 {
411 logf(" module 0 reset");
412 return 1;
413 }
414 }
415
416 logf(" reset failed");
417 return 0;
418}
419
420int syn_init(void)
421{
422 syn_flush();
423 return syn_reset();
424}
425
426#ifdef ROCKBOX_HAS_LOGF
427void syn_info(void)
428{
429 int i, val;
430 int data[8];
431
432 logf("syn_info...");
433
434 /* module base info */
435 logf("module base info:");
436 data[0] = MEP_READ;
437 data[1] = 0x80;
438 syn_send_data(data, 2);
439 val = syn_read_device(data, 8);
440 if (val > 0)
441 {
442 for (i = 0; i < 8; i++)
443 logf(" data[%d] = 0x%02x", i, data[i]);
444 }
445
446 /* module product info */
447 logf("module product info:");
448 data[0] = MEP_READ;
449 data[1] = 0x81;
450 syn_send_data(data, 2);
451 val = syn_read_device(data, 8);
452 if (val > 0)
453 {
454 for (i = 0; i < 8; i++)
455 logf(" data[%d] = 0x%02x", i, data[i]);
456 }
457
458 /* module serialization */
459 logf("module serialization:");
460 data[0] = MEP_READ;
461 data[1] = 0x82;
462 syn_send_data(data, 2);
463 val = syn_read_device(data, 8);
464 if (val > 0)
465 {
466 for (i = 0; i < 8; i++)
467 logf(" data[%d] = 0x%02x", i, data[i]);
468 }
469
470 /* 1-D sensor info */
471 logf("1-d sensor info:");
472 data[0] = MEP_READ;
473 data[1] = 0x80 + 0x20;
474 syn_send_data(data, 2);
475 val = syn_read_device(data, 8);
476 if (val > 0)
477 {
478 for (i = 0; i < 8; i++)
479 logf(" data[%d] = 0x%02x", i, data[i]);
480 }
481}
482#endif