From f2b66918e635dbd4860bf3af4303d2e86d182983 Mon Sep 17 00:00:00 2001 From: Mark Arigo Date: Wed, 17 Dec 2008 04:38:53 +0000 Subject: 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 --- firmware/drivers/synaptics-mep.c | 482 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 firmware/drivers/synaptics-mep.c (limited to 'firmware/drivers/synaptics-mep.c') 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 @@ +/*************************************************************************** + * __________ __ ___. + * 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 +#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 -- cgit v1.2.3