summaryrefslogtreecommitdiff
path: root/firmware/drivers
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
parenta742c3cd56d802f72567167e6e5482308744de3e (diff)
downloadrockbox-f2b66918e635dbd4860bf3af4303d2e86d182983.zip
rockbox-f2b66918e635dbd4860bf3af4303d2e86d182983.tar.gz
rockbox-f2b66918e635dbd4860bf3af4303d2e86d182983.tar.bz2
rockbox-f2b66918e635dbd4860bf3af4303d2e86d182983.tar.xz
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')
-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 0000000..44b1860
--- /dev/null
+++ b/firmware/drivers/synaptics-mep.c
@@ -0,0 +1,482 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2008 by Mark Arigo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include "config.h"
+#include "cpu.h"
+#include "system.h"
+#include "kernel.h"
+#include "system.h"
+
+#define LOGF_ENABLE
+#include "logf.h"
+
+/* Driver for the Synaptics Touchpad based on the "Synaptics Modular Embedded
+ Protocol: 3-Wire Interface Specification" documentation */
+
+#define ACK (GPIOD_INPUT_VAL & 0x1)
+#define ACK_HI GPIOD_OUTPUT_VAL |= 0x1
+#define ACK_LO GPIOD_OUTPUT_VAL &= ~0x1
+
+#define CLK ((GPIOD_INPUT_VAL & 0x2) >> 1)
+#define CLK_HI GPIOD_OUTPUT_VAL |= 0x2
+#define CLK_LO GPIOD_OUTPUT_VAL &= ~0x2
+
+#define DATA ((GPIOD_INPUT_VAL & 0x4) >> 2)
+#define DATA_HI GPIOD_OUTPUT_EN |= 0x4; GPIOD_OUTPUT_VAL |= 0x4
+#define DATA_LO GPIOD_OUTPUT_EN |= 0x4; GPIOD_OUTPUT_VAL &= ~0x4
+#define DATA_CL GPIOD_OUTPUT_EN &= ~0x4
+
+#define LO 0
+#define HI 1
+
+#define READ_RETRY 8
+#define READ_ERROR -1
+
+#define MEP_HELLO_HEADER 0x19
+#define MEP_HELLO_ID 0x1
+
+#define MEP_READ 0x1
+#define MEP_WRITE 0x3
+
+static int syn_wait_clk_change(unsigned int val)
+{
+ int i;
+
+ for (i = 0; i < 10000; i++)
+ {
+ if (CLK == val)
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int syn_get_data(void)
+{
+ DATA_CL;
+ return DATA;
+}
+
+static void syn_wait_guest_flush(void)
+{
+ /* Flush receiving (flushee) state:
+ handshake until DATA goes high during P3 stage */
+ if (CLK == LO)
+ {
+ ACK_HI; /* P1 -> P2 */
+ syn_wait_clk_change(HI); /* P2 -> P3 */
+ }
+
+ while (syn_get_data() == LO)
+ {
+ ACK_HI; /* P3 -> P0 */
+ syn_wait_clk_change(LO); /* P0 -> P1 */
+ ACK_LO; /* P1 -> P2 */
+ syn_wait_clk_change(HI); /* P2 -> P3 */
+ }
+
+ /* Continue handshaking until back to P0 */
+ ACK_HI; /* P3 -> P0 */
+}
+
+static void syn_flush(void)
+{
+ int i;
+
+ logf("syn_flush...");
+
+ /* Flusher holds DATA low for at least 36 handshake cycles */
+ DATA_LO;
+
+ for (i = 0; i < 36; i++)
+ {
+ syn_wait_clk_change(LO); /* P0 -> P1 */
+ ACK_LO; /* P1 -> P2 */
+ syn_wait_clk_change(HI); /* P2 -> P3 */
+ ACK_HI; /* P3 -> P0 */
+ }
+
+ /* Raise DATA in P1 stage */
+ syn_wait_clk_change(LO); /* P0 -> P1 */
+ DATA_HI;
+
+ /* After a flush, the flushing device enters a flush-receiving (flushee)
+ state */
+ syn_wait_guest_flush();
+}
+
+static int syn_send_data(int *data, int len)
+{
+ int i, bit;
+ int parity = 0;
+
+ logf("syn_send_data...");
+
+ /* 1. Lower DATA line to issue a request-to-send to guest */
+ DATA_LO;
+
+ /* 2. Wait for guest to lower CLK */
+ syn_wait_clk_change(LO);
+
+ /* 3. Lower ACK (with DATA still low) */
+ ACK_LO;
+
+ /* 4. Wait for guest to raise CLK */
+ syn_wait_clk_change(HI);
+
+ /* 5. Send data */
+ for (i = 0; i < len; i++)
+ {
+ logf(" sending byte: %d", data[i]);
+
+ bit = 0;
+ while (bit < 8)
+ {
+ /* 5a. Drive data low if bit is 0, or high if bit is 1 */
+ if (data[i] & (1 << bit))
+ {
+ DATA_HI;
+ parity++;
+ }
+ else
+ {
+ DATA_LO;
+ }
+ bit++;
+
+ /* 5b. Invert ACK to indicate that the data bit is ready */
+ ACK_HI;
+
+ /* 5c. Wait for guest to invert CLK */
+ syn_wait_clk_change(LO);
+
+ /* Repeat for next bit */
+ if (data[i] & (1 << bit))
+ {
+ DATA_HI;
+ parity++;
+ }
+ else
+ {
+ DATA_LO;
+ }
+ bit++;
+
+ ACK_LO;
+
+ syn_wait_clk_change(HI);
+ }
+ }
+
+ /* 7. Transmission termination sequence: */
+ /* 7a. Host may put parity bit on DATA. Hosts that do not generate
+ parity should set DATA high. Parity is 1 if there's an odd
+ number of '1' bits, or 0 if there's an even number of '1' bits. */
+ parity = parity % 2;
+ if (parity)
+ {
+ DATA_HI;
+ }
+ else
+ {
+ DATA_LO;
+ }
+ logf(" send parity = %d", parity);
+
+ /* 7b. Raise ACK to indicate that the optional parity bit is ready */
+ ACK_HI;
+
+ /* 7c. Guest lowers CLK */
+ syn_wait_clk_change(LO);
+
+ /* 7d. Pull DATA high (if parity bit was 0) */
+ DATA_HI;
+
+ /* 7e. Lower ACK to indicate that the stop bit is ready */
+ ACK_LO;
+
+ /* 7f. Guest raises CLK */
+ syn_wait_clk_change(HI);
+
+ /* 7g. If DATA is low, guest is flushing this transfer. Host should
+ enter the flushee state. */
+ if (syn_get_data() == LO)
+ {
+ logf(" module flushing");
+
+ syn_wait_guest_flush();
+ return -1;
+ }
+
+ /* 7h. Host raises ACK and the link enters the idle state */
+ ACK_HI;
+
+ return len;
+}
+
+static int syn_read_data(int *data, int data_len)
+{
+ int i, len, bit, parity, tmp;
+ int *data_ptr;
+
+ logf("syn_read_data...");
+
+ /* 1. Guest drives CLK low */
+ if (CLK != LO)
+ return 0;
+
+ /* 1a. If the host is willing to receive a packet it lowers ACK */
+ ACK_LO;
+
+ /* 2. Guest may issue a request-to-send by lowering DATA. If the
+ guest decides not to transmit a packet, it may abort the
+ transmission by not lowering DATA. */
+
+ /* 3. The guest raises CLK */
+ syn_wait_clk_change(HI);
+
+ /* 4. If the guest is still driving DATA low, the transfer is commited
+ to occur. Otherwise, the transfer is aborted. In either case,
+ the host raises ACK. */
+ if (syn_get_data() == HI)
+ {
+ logf(" read abort");
+
+ ACK_HI;
+ return READ_ERROR;
+ }
+ else
+ {
+ ACK_HI;
+ }
+
+ /* 5. Read the incoming data packet */
+ i = 0;
+ len = 0;
+ parity = 0;
+ while (i <= len)
+ {
+ bit = 0;
+
+ if (i < data_len)
+ data_ptr = &data[i];
+ else
+ data_ptr = &tmp;
+
+ *data_ptr = 0;
+ while (bit < 8)
+ {
+ /* 5b. Guset inverts CLK to indicate that data is ready */
+ syn_wait_clk_change(LO);
+
+ /* 5d. Read the data bit from DATA */
+ if (syn_get_data() == HI)
+ {
+ *data_ptr |= (1 << bit);
+ parity++;
+ }
+ bit++;
+
+ /* 5e. Invert ACK to indicate that data has been read */
+ ACK_LO;
+
+ /* Repeat for next bit */
+ syn_wait_clk_change(HI);
+
+ if (syn_get_data() == HI)
+ {
+ *data_ptr |= (1 << bit);
+ parity++;
+ }
+ bit++;
+
+ ACK_HI;
+ }
+
+ /* First byte is the packet header */
+ if (i == 0)
+ {
+ /* Format control (bit 3) should be 1 */
+ if (*data_ptr & 0x8)
+ {
+ /* Packet length is bits 0:2 */
+ len = *data_ptr & 0x7;
+ logf(" packet length = %d", len);
+ }
+ else
+ {
+ logf(" invalid format ctrl bit");
+ return READ_ERROR;
+ }
+ }
+
+ i++;
+ }
+
+ /* 7. Transmission termination cycle */
+ /* 7a. The guest generates a parity bit on DATA */
+ /* 7b. The host waits for guest to lower CLK */
+ syn_wait_clk_change(LO);
+
+ /* 7c. The host verifies the parity bit is correct */
+ parity = parity % 2;
+ logf(" parity check: %d / %d", syn_get_data(), parity);
+
+ /* TODO: parity error handling */
+
+ /* 7d. The host lowers ACK */
+ ACK_LO;
+
+ /* 7e. The host waits for the guest to raise CLK indicating
+ that the stop bit is ready */
+ syn_wait_clk_change(HI);
+
+ /* 7f. The host reads DATA and verifies that it is 1 */
+ if (syn_get_data() == LO)
+ {
+ logf(" framing error");
+
+ ACK_HI;
+ return READ_ERROR;
+ }
+
+ ACK_HI;
+
+ return len;
+}
+
+int syn_read_device(int *data, int len)
+{
+ int i;
+ int ret = READ_ERROR;
+
+ for (i = 0; i < READ_RETRY; i++)
+ {
+ if (syn_wait_clk_change(LO))
+ {
+ /* module is sending data */
+ ret = syn_read_data(data, len);
+ if (ret != READ_ERROR)
+ return ret;
+
+ syn_flush();
+ }
+ else
+ {
+ /* module is idle */
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+static int syn_reset(void)
+{
+ int val, id;
+ int data[2];
+
+ logf("syn_reset...");
+
+ /* reset module 0 */
+ val = (0 << 4) | (1 << 3) | 0;
+ syn_send_data(&val, 1);
+
+ val = syn_read_device(data, 2);
+ if (val == 1)
+ {
+ val = data[0] & 0xff; /* packet header */
+ id = (data[1] >> 4) & 0xf; /* packet id */
+ if ((val == MEP_HELLO_HEADER) && (id == MEP_HELLO_ID))
+ {
+ logf(" module 0 reset");
+ return 1;
+ }
+ }
+
+ logf(" reset failed");
+ return 0;
+}
+
+int syn_init(void)
+{
+ syn_flush();
+ return syn_reset();
+}
+
+#ifdef ROCKBOX_HAS_LOGF
+void syn_info(void)
+{
+ int i, val;
+ int data[8];
+
+ logf("syn_info...");
+
+ /* module base info */
+ logf("module base info:");
+ data[0] = MEP_READ;
+ data[1] = 0x80;
+ syn_send_data(data, 2);
+ val = syn_read_device(data, 8);
+ if (val > 0)
+ {
+ for (i = 0; i < 8; i++)
+ logf(" data[%d] = 0x%02x", i, data[i]);
+ }
+
+ /* module product info */
+ logf("module product info:");
+ data[0] = MEP_READ;
+ data[1] = 0x81;
+ syn_send_data(data, 2);
+ val = syn_read_device(data, 8);
+ if (val > 0)
+ {
+ for (i = 0; i < 8; i++)
+ logf(" data[%d] = 0x%02x", i, data[i]);
+ }
+
+ /* module serialization */
+ logf("module serialization:");
+ data[0] = MEP_READ;
+ data[1] = 0x82;
+ syn_send_data(data, 2);
+ val = syn_read_device(data, 8);
+ if (val > 0)
+ {
+ for (i = 0; i < 8; i++)
+ logf(" data[%d] = 0x%02x", i, data[i]);
+ }
+
+ /* 1-D sensor info */
+ logf("1-d sensor info:");
+ data[0] = MEP_READ;
+ data[1] = 0x80 + 0x20;
+ syn_send_data(data, 2);
+ val = syn_read_device(data, 8);
+ if (val > 0)
+ {
+ for (i = 0; i < 8; i++)
+ logf(" data[%d] = 0x%02x", i, data[i]);
+ }
+}
+#endif