Adds various OMAP input drivers.
Signed-off-by: Tony Lindgren <tony@atomide.com>
to your machine, so normally you should say Y here.
endif
+
+config KEYBOARD_OMAP
+ tristate "TI OMAP keypad support"
+ depends on (ARCH_OMAP1510 || ARCH_OMAP16XX) && INPUT && INPUT_KEYBOARD
+ help
+ Say Y here if you want to use the OMAP keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap-keypad.
+
+config OMAP_PS2
+ tristate "TI OMAP Innovator 1510 PS/2 keyboard & mouse support"
+ depends on ARCH_OMAP1510 && MACH_OMAP_INNOVATOR && INPUT && INPUT_KEYBOARD
+ help
+ Say Y here if you want to use the OMAP Innovator 1510 PS/2
+ keyboard and mouse.
+
+ To compile this driver as a module, choose M here: the
+ module will be called innovator_ps2.
+
obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
-
+obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+obj-$(CONFIG_OMAP_PS2) += innovator_ps2.o
--- /dev/null
+/*
+ * drivers/char/innovator_ps2.c
+ *
+ * Basic PS/2 keyboard/mouse driver for the Juno® USAR HID controller
+ * present on the TI Innovator/OMAP1510 Break-out-board.
+ *
+ *
+ * Author: MontaVista Software, Inc.
+ * <gdavis@mvista.com> or <source@mvista.com>
+ *
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ *
+ * REFERENCES:
+ *
+ * 1. Technical Reference Manual
+ * Juno® 01
+ * Multi-function ICs family
+ * UR8HC007-001 HID & Power management controller
+ * Document Number: DOC8-007-001-TR-075
+ * Date: February 2002
+ * Copyright ©1998-2002 Semtech Corporation
+ * http://www.semtech.com/pdf/doc8-007-001-tr.pdf
+ *
+ * 2. Juno® 01 UR8HC007-001 Data Sheet
+ * Extremely Low-power Input Device and Power Management IC
+ * Copyright ©1998-2002 Semtech Corporation
+ * DOC8-007-001-DS-112
+ * http://www.semtech.com/pdf/doc8-007-001-ds.pdf
+ *
+ *
+ * HISTORY:
+ *
+ * 20030626: George G. Davis <gdavis@mvista.com>
+ * Initially based on the following RidgeRun DSPlinux Version 1.6 files:
+ * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_hid.c
+ * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_hid.h
+ * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_ps2.c
+ * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_spi.c
+ * All original files above are
+ * Copyright (C) 2001 RidgeRun, Inc.
+ * Author: Alex McMains <aam@ridgerun.com>
+ *
+ * 20040812: Thiago Radicchi <trr@dcc.ufmg.br>
+ * Cleanup of old code from 2.4 driver and some debug code.
+ * Minor changes in interrupt handling code.
+ *
+ * NOTES:
+ *
+ * 1. This driver does not provide support for setting keyboard/mouse
+ * configuration parameters. Both devices are managed directly by
+ * the Juno UR8HC007-001 on behalf of the host. This minimises the
+ * amount of host processing required to manage HID events and state
+ * changes, e.g. both keyboard and mouse devices are hot pluggable
+ * with no host intervention required. However, we cannot customise
+ * keyboard/mouse settings in this case. So we live with the defaults
+ * as setup by the Juno UR8HC007-001 whatever they may be.
+ * 2. Keyboard auto repeat does not work. See 1 above. : )
+ *
+ *
+ * TODO:
+ *
+ * 1. Complete DPM/LDM stubs and test.
+ * 2. Add SPI error handling support, i.e. resend, etc.,.
+ * 3. Determine why innovator_hid_interrupt() is called for every
+ * invocation of Innovator FPGA IRQ demux. It appears that the
+ * missed Innovator ethernet workaround may be to blame. However,
+ * it does not adversely affect operation of this driver since we
+ * check for assertion of ATN prior to servicing the interrupt. If
+ * ATN is negated, we bug out right away.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/arch/fpga.h>
+
+#undef INNOVATOR_KEYB_DEBUG
+#ifdef INNOVATOR_KEYB_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG "%s:%d: " format , \
+ __FUNCTION__ , __LINE__ , ## arg)
+#define entry() printk(KERN_DEBUG "%s:%d: Entry\n" , __FUNCTION__ , __LINE__)
+#define exit() printk(KERN_DEBUG "%s:%d: Exit\n" , __FUNCTION__ , __LINE__)
+#define dump_packet(p, n) \
+ { \
+ int i; \
+ printk(KERN_DEBUG "%s:%d: %08x:" , \
+ __FUNCTION__ , __LINE__ , (int) p); \
+ for (i = 0; i < n; i += 1) { \
+ printk(" %02x", (int) p[i]); \
+ } \
+ printk("\n"); \
+ }
+#else
+#define dbg(format, arg...) do {} while (0)
+#define entry() do {} while (0)
+#define exit() do {} while (0)
+#define dump_packet(p, n) do {} while (0)
+#endif
+
+
+#define PFX "innovator_ps2"
+#define err(format, arg...) printk(KERN_ERR PFX ": " format , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format , ## arg)
+
+
+/****************************************************************************/
+
+/*
+ * Synchronous communications timing parameters (Reference [1] pg 7-7)
+ */
+
+#define tMSA 5000 /* -/5ms _SS to _ATN (master transfer) */
+#define tMAC 100 /* 100us/5ms _ATN to first clock pulse (master
+ transfer) */
+#define tMIB 150 /* 150us/5ms Beginning of byte transfer to beginning
+ of next byte transfer */
+#define tSIB 150 /* 150us/5ms Beginning of byte transfer to beginning
+ of next byte transfer */
+#define tMSP 100 /* -/100us Last clock pulse of packet to _SS
+ de-assertion */
+#define tMNSA 100 /* -/100us _SS de-assertion to _ATN de-assertion */
+#define tMNEXT 120 /* 120uS/- _ATN release to _SS re-assertion
+ (master transfer) */
+#define tSAS 5000 /* -/5ms _ATN to _SS (slave transfer) */
+#define tSSC 100 /* 100us/5ms _SS to first clock pulse (slave
+ transfer) */
+#define tSNA 100 /* -/100us Last clock pulse of packet to _ATN
+ de-assertion */
+#define tSNAS 100 /* -/100us _ATN release to _SS de-assertion */
+#define tSNEXT 120 /* 120us/- _SS release to _ATN re-assertion
+ (slave transfer) */
+#define tSCK 4 /* 4us/- Clock period */
+#define tSLOW 2 /* 2us/- Clock LOW period */
+#define tHOLD 200 /* 200ns/- Master data hold time */
+#define tSETUP 100 /* 100ns/- Master data setup Time */
+#define tSSETUP 500 /* -/500ns Slave data setup time from clock
+ falling edge */
+
+
+/*
+ * Protocol Headers (Reference [1], pg. 5-1):
+ */
+
+
+/* Protocols used in commands issued by the host: */
+#define SIMPLE 0x80 /* Simple commands
+ * Common for both host and controller
+ * protocol headers.
+ */
+#define WRITE_REGISTER_BIT 0x81 /* Write register bit */
+#define READ_REGISTER_BIT 0x82 /* Read register bit */
+#define WRITE_REGISTER 0x83 /* Write register */
+#define READ_REGISTER 0x84 /* Read register */
+#define WRITE_BLOCK 0x85 /* Write block */
+#define READ_BLOCK 0x86 /* Read block */
+
+
+/* Protocols used in responses, reports and alerts issued by the controller: */
+#define REPORT_REGISTER_BIT 0x81 /* Report register bit & event alerts */
+#define REPORT_REGISTER 0x83 /* Report register */
+#define REPORT_BLOCK 0x85 /* Report block */
+#define POINTING_REPORT 0x87 /* Pointing device data report */
+#define KEYBOARD_REPORT 0x88 /* Keyboard device data report */
+
+
+/* Simple Commands (Reference [1], pg 5-3): */
+#define INITIALIZE 0x00 /* Forces the recipient to enter the
+ * known default power-on state.
+ */
+#define INITIALIZATION_COMPLETE 0x01 /* Issued as a hand-shake response only
+ * to the "Initialize" command.
+ */
+#define RESEND_REQUEST 0x05 /* Issued upon error in the reception
+ * of a package. The recipient resends
+ * the last transmitted packet.
+ */
+
+/* Register offsets (Reference [1], pg 6-1 thru 6-9): */
+
+#define REG_PM_COMM 0
+#define REG_PM_STATUS 1
+#define REG_PAGENO 255
+
+/* Power management bits ((Reference [1], pg 6-10): */
+
+#define SUS_STATE 0x2 /* in REG_PM_COMM */
+
+/* Miscellaneous constants: */
+
+#define X_MSB_SHIFT (8-4)
+#define X_MSB_MASK (3<<4)
+#define Y_MSB_SHIFT (8-6)
+#define Y_MSB_MASK (3<<6)
+
+
+#define JUNO_BLOCK_SIZE 32
+#define JUNO_BUFFER_SIZE 256
+
+
+/*
+ * Errors:
+ */
+
+#define E_BAD_HEADER 1
+#define E_BAD_LRC 2
+#define E_ZERO_BYTES 3
+#define E_BAD_VALUE 4
+#define E_BAD_MODE 5
+#define E_REPORT_MODE 6
+#define E_BAD_ACK 7
+#define E_BAD_DEVICE_ID 8
+#define E_PKT_SZ 9
+
+
+/*
+ * Host/Controller Command/Response Formats:
+ */
+
+typedef struct _simple_t {
+ u8 header;
+ u8 cmd_code;
+ u8 LRC;
+} __attribute__ ((packed)) simple_t;
+
+typedef struct _write_bit_t {
+ u8 header;
+ u8 offset;
+ u8 value_bit;
+ u8 LRC;
+} __attribute__ ((packed)) write_bit_t;
+
+typedef struct _read_bit_t {
+ u8 header;
+ u8 offset;
+ u8 bit;
+ u8 LRC;
+} __attribute__ ((packed)) read_bit_t;
+
+typedef struct _write_reg_t {
+ u8 header;
+ u8 offset;
+ u8 value;
+ u8 LRC;
+} __attribute__ ((packed)) write_reg_t;
+
+typedef struct _read_reg_t {
+ u8 header;
+ u8 offset;
+ u8 LRC;
+} __attribute__ ((packed)) read_reg_t;
+
+typedef struct _write_block_t {
+ u8 header;
+ u8 offset;
+ u8 length;
+ u8 block[JUNO_BLOCK_SIZE + 1]; /* Hack: LRC is last element of block[] */
+} __attribute__ ((packed)) write_block_t;
+
+typedef struct _read_block_t {
+ u8 header;
+ u8 offset;
+ u8 length;
+ u8 LRC;
+} __attribute__ ((packed)) read_block_t;
+
+typedef struct _report_bit_t {
+ u8 header;
+ u8 offset;
+ u8 value_bit;
+ u8 LRC;
+} __attribute__ ((packed)) report_bit_t;
+
+typedef struct _report_reg_t {
+ u8 header;
+ u8 offset;
+ u8 value;
+ u8 LRC;
+} __attribute__ ((packed)) report_reg_t;
+
+typedef struct _report_block_t {
+ u8 header;
+ u8 offset;
+ u8 length;
+ u8 block[32];
+ u8 LRC;
+} __attribute__ ((packed)) report_block_t;
+
+typedef struct _mse_report_t {
+ u8 header;
+ u8 buttons;
+ u8 Xdisplacement;
+ u8 Ydisplacement;
+ u8 Zdisplacement;
+ u8 LRC;
+} __attribute__ ((packed)) mse_report_t;
+
+typedef struct _kdb_report_t {
+ u8 header;
+ u8 keynum; /* up > 0x80, down < 0x7E, all keys up 0x00 */
+ u8 LRC;
+} __attribute__ ((packed)) kdb_report_t;
+
+
+static u8 buffer[JUNO_BUFFER_SIZE];
+static u8 block[JUNO_BLOCK_SIZE];
+
+static void do_hid_tasklet(unsigned long);
+DECLARE_TASKLET(hid_tasklet, do_hid_tasklet, 0);
+static struct innovator_hid_dev *hid;
+static spinlock_t innovator_fpga_hid_lock = SPIN_LOCK_UNLOCKED;
+static atomic_t innovator_fpga_hid_busy = ATOMIC_INIT(0);
+
+struct innovator_hid_dev {
+ struct input_dev mouse, keyboard;
+ int open;
+ int irq_enabled;
+};
+
+/****************************************************************************/
+
+/*
+ * Low-level TI Innovator/OMAP1510 FPGA HID SPI interface helper functions:
+ */
+
+static u8
+innovator_fpga_hid_rd(void)
+{
+ u8 val = inb(INNOVATOR_FPGA_HID_SPI);
+ return val;
+}
+
+static void
+innovator_fpga_hid_wr(u8 val)
+{
+ outb(val, INNOVATOR_FPGA_HID_SPI);
+}
+
+static void
+innovator_fpga_hid_frob(u8 mask, u8 val)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ innovator_fpga_hid_wr((innovator_fpga_hid_rd() & ~mask) | val);
+ local_irq_restore(flags);
+}
+
+static void
+innovator_fpga_hid_set_bits(u8 x)
+{
+ innovator_fpga_hid_frob(x, x);
+}
+
+static void
+innovator_fpga_hid_clear_bits(u8 x)
+{
+ innovator_fpga_hid_frob(x, 0);
+}
+
+static void
+SS(int value)
+{
+ innovator_fpga_hid_frob(OMAP1510_FPGA_HID_nSS, value ? OMAP1510_FPGA_HID_nSS : 0);
+}
+
+static void
+SCLK(int value)
+{
+ innovator_fpga_hid_frob(OMAP1510_FPGA_HID_SCLK, value ? OMAP1510_FPGA_HID_SCLK : 0);
+}
+
+static void
+MOSI(int value)
+{
+ innovator_fpga_hid_frob(OMAP1510_FPGA_HID_MOSI, value ? OMAP1510_FPGA_HID_MOSI : 0);
+}
+
+static u8
+MISO(void)
+{
+ return ((innovator_fpga_hid_rd() & OMAP1510_FPGA_HID_MISO) ? 1 : 0);
+}
+
+static u8
+ATN(void)
+{
+ return ((innovator_fpga_hid_rd() & OMAP1510_FPGA_HID_ATN) ? 1 : 0);
+}
+
+static int
+wait_for_ATN(int assert, int timeout)
+{
+ do {
+ if (ATN() == assert)
+ return 0;
+ udelay(1);
+ } while (timeout -= 1);
+ return -1;
+}
+
+static u8
+innovator_fpga_hid_xfer_byte(u8 xbyte)
+{
+ int i;
+ u8 rbyte;
+
+ for (rbyte = 0, i = 7; i >= 0; i -= 1) {
+ SCLK(0);
+ MOSI((xbyte >> i) & 1);
+ udelay(tSLOW);
+ SCLK(1);
+ rbyte = (rbyte << 1) | MISO();
+ udelay(tSLOW);
+ }
+
+ return rbyte;
+}
+
+static void
+innovator_fpga_hid_reset(void)
+{
+ innovator_fpga_hid_wr(OMAP1510_FPGA_HID_SCLK | OMAP1510_FPGA_HID_MOSI);
+ mdelay(1);
+ innovator_fpga_hid_set_bits(OMAP1510_FPGA_HID_RESETn);
+}
+
+
+/*****************************************************************************
+
+ Refer to Reference [1], Chapter 7 / Low-level communications, Serial
+ Peripheral Interface (SPI) implementation Host (master) packet
+ transmission timing, pg. 7-3, for timing and implementation details
+ for spi_xmt().
+
+ *****************************************************************************/
+
+int
+spi_xmt(u8 * p, u8 n)
+{
+ unsigned long flags;
+
+ dump_packet(p, n);
+ local_irq_save(flags);
+ disable_irq(OMAP1510_INT_FPGA_ATN);
+
+ if (ATN()) {
+ /* Oops, we have a collision. */
+ enable_irq(OMAP1510_INT_FPGA_ATN);
+ local_irq_restore(flags);
+ dbg("Protocol error: ATN is asserted\n");
+ return -EAGAIN;
+ }
+
+ SS(1);
+
+ if (wait_for_ATN(1, tMSA) < 0) {
+ SS(0);
+ enable_irq(OMAP1510_INT_FPGA_ATN);
+ local_irq_restore(flags);
+ dbg("timeout waiting for ATN assertion\n");
+ return -EREMOTEIO;
+ }
+
+ udelay(tMAC);
+
+ while (n--) {
+ innovator_fpga_hid_xfer_byte(*p++);
+ if (n) {
+ udelay(tMIB - 8 * tSCK);
+ }
+ }
+
+ MOSI(1); /* Set MOSI to idle high. */
+
+ /* NOTE: The data sheet does not specify a minimum delay
+ * here. But innovator_fpga_hid_xfer_byte() gives us a half-clock
+ * delay (tSLOW) after the last bit is sent. So I'm happy with
+ * that.
+ */
+
+ SS(0);
+
+ if (wait_for_ATN(0, tMNSA) < 0) {
+ enable_irq(OMAP1510_INT_FPGA_ATN);
+ local_irq_restore(flags);
+ dbg("timeout waiting for ATN negation\n");
+ return -EREMOTEIO;
+ }
+
+ udelay(tMNEXT);
+ enable_irq(OMAP1510_INT_FPGA_ATN);
+ local_irq_restore(flags);
+ return 0;
+}
+
+
+/*****************************************************************************
+
+ Refer to Reference [1], Chapter 7 / Low-level communications, Serial
+ Peripheral Interface (SPI) implementation, Slave packet transmission
+ timing, pg. 7-5, for timing and implementation details for spi_rcv().
+
+ *****************************************************************************/
+
+int
+spi_rcv(u8 * p, int len)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ if (len > 256) {
+ /* Limit packet size to something reasonable */
+ return -1;
+ }
+
+ local_irq_save(flags);
+
+ if (wait_for_ATN(1, tMSA) < 0) {
+ local_irq_restore(flags);
+ dbg("Protocol error: ATN is not asserted\n");
+ return -EREMOTEIO;
+ }
+
+ SS(1);
+
+ udelay(tSSC);
+
+ while (ATN()) {
+ if (ret >= len) {
+ err("over run error\n");
+ ret = -1;
+ break;
+ }
+ p[ret++] = innovator_fpga_hid_xfer_byte(0xff);
+ udelay(tSNA); /* Wait long enough to detect negation of ATN
+ * after last clock pulse of packet.
+ *
+ * NOTE: Normally, we need a minimum delay of
+ * tSIB between the start of one byte
+ * and the start of the next. However,
+ * we also need to wait long enough
+ * for the USAR to negate ATN before
+ * starting the next byte. So we use
+ * max(tSIB - 8 * tSCK, tSNA) here to
+ * satisfy both constraints.
+ */
+ }
+
+ SS(0); /* NOTE: The data sheet does not specify a minimum delay
+ * here. But innovator_fpga_hid_xfer_byte() gives us a
+ * half-clock delay (tSLOW) after the last bit is sent. So
+ * I'm happy with that (rather than no delay at all : ).
+ */
+
+
+ udelay(tSNEXT); /* This isn't quite right. Assertion of ATN after
+ * negation of SS is an USAR timing constraint.
+ * What we need here is a spec for the minimum
+ * delay from SS negation to SS assertion. But
+ * for now, just use this brain dead delay.
+ */
+
+ local_irq_restore(flags);
+
+ if (ret > 0) {
+ dump_packet(p, ret);
+ }
+
+ return ret;
+}
+
+
+/*****************************************************************************
+ Calculate Host/Controller Command/Response Longitudinal Redundancy Check (LRC)
+
+ The algorithm implemented in calculate_LRC() below is taken directly from
+ the reference [1], Chapter 7 / Low-level communications, LRC (Longitudinal
+ Redundancy Check), pg 5-10.
+
+ *****************************************************************************/
+
+static u8
+calculate_LRC(u8 * p, int n)
+{
+ u8 LRC;
+ int i;
+
+ /*
+ * Init the LRC using the first two message bytes.
+ */
+ LRC = p[0] ^ p[1];
+
+ /*
+ * Update the LRC using the remainder of the p.
+ */
+ for (i = 2; i < n; i++)
+ LRC ^= p[i];
+
+ /*
+ * If the MSB is set then clear the MSB and change the next
+ * most significant bit
+ */
+ if (LRC & 0x80)
+ LRC ^= 0xC0;
+
+ return LRC;
+}
+
+
+/*
+ * Controller response helper functions:
+ */
+
+static inline int
+report_mouse(mse_report_t * p, int n)
+{
+ if (p->header != POINTING_REPORT)
+ return -E_BAD_HEADER;
+
+ if (n != sizeof(mse_report_t))
+ return -E_PKT_SZ;
+
+ return (p->LRC != calculate_LRC((u8 *) p, sizeof(mse_report_t) - 1)) ?
+ -E_BAD_LRC : POINTING_REPORT;
+}
+
+static inline int
+report_keyboard(kdb_report_t * p, int n)
+{
+ if (p->header != KEYBOARD_REPORT)
+ return -E_BAD_HEADER;
+
+ if (n != sizeof(kdb_report_t))
+ return -E_PKT_SZ;
+
+ return (p->LRC != calculate_LRC((u8 *) p, sizeof(kdb_report_t) - 1)) ?
+ -E_BAD_LRC : KEYBOARD_REPORT;
+}
+
+
+/*
+ * Miscellaneous helper functions:
+ */
+
+static inline int
+report_type(u8 * type)
+{
+ /* check the header to find out what kind of report it is */
+ if ((*type) == KEYBOARD_REPORT)
+ return KEYBOARD_REPORT;
+ else if ((*type) == POINTING_REPORT)
+ return POINTING_REPORT;
+ else
+ return -E_BAD_HEADER;
+}
+
+static inline int
+report_async(void * p, int n)
+{
+ int ret;
+
+ if ((ret = spi_rcv((u8 *) p, n)) < 0)
+ return ret;
+
+ if (report_type((u8 *) p) == POINTING_REPORT)
+ ret = report_mouse((mse_report_t *) p, ret);
+ else if (report_type((u8 *) p) == KEYBOARD_REPORT)
+ ret = report_keyboard((kdb_report_t *) p, ret);
+
+ return ret;
+}
+
+static int
+verify_init(u8 * p)
+{
+ return (((simple_t *)p)->cmd_code == 0x01) ? 0 : -1;
+}
+
+
+/*
+ * Host command helper functions:
+ */
+
+#if 0
+/* REVISIT/TODO: Wrapper for command/response with resend handing. */
+static int
+spi_xfer(u8 * optr, u8 osz, u8 * iptr, u8 isz)
+{
+ static u8 buf[256];
+ int ret;
+ int xretries = 3;
+
+ do {
+ if (optr != NULL && osz) {
+ do {
+ ret = spi_xmt((u8 *) optr, osz);
+ } while (ret < 0);
+ }
+
+ ret = spi_rcv((u8 *) buf, 256);
+
+ if (ret == -EREMOTEIO) {
+ if (iptr == NULL) {
+ break;
+ }
+ }
+ } while (xretries--);
+
+ return ret;
+}
+#endif
+
+/* REVISIT: Enable these when/if additional Juno features are required. */
+static inline int
+simple(u8 cmd)
+{
+ static simple_t p;
+ int ret;
+
+ p.header = SIMPLE;
+ p.cmd_code = cmd;
+ p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+ if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+ return ret;
+
+ if ((ret = spi_rcv((u8 *) & p, sizeof(p))) < 0)
+ return ret;
+
+ if (ret == 0)
+ return -E_ZERO_BYTES;
+
+ if (ret != sizeof(p))
+ return -E_PKT_SZ;
+
+ if (p.header != SIMPLE)
+ return -E_BAD_HEADER;
+
+ if (p.LRC != calculate_LRC((u8 *) & p, sizeof(p) - 1))
+ return -E_BAD_LRC;
+
+ /* REVISIT: Need to check or return response code here? */
+}
+
+static inline int
+write_bit(u8 offset, u8 bit, u8 value)
+{
+ static write_bit_t p;
+
+ p.header = WRITE_REGISTER_BIT;
+ p.offset = offset;
+ p.value_bit = (bit << 1) | (value & 1);
+ p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+ return spi_xmt((u8 *) & p, sizeof(p));
+}
+
+static inline int
+read_bit(u8 offset, u8 bit, u8 * data)
+{
+ static read_bit_t p;
+ static report_bit_t q;
+ int ret;
+
+ p.header = READ_REGISTER_BIT;
+ p.offset = offset;
+ p.bit = bit;
+ p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+ if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+ return ret;
+
+ if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0)
+ return ret;
+
+ if (ret == 0)
+ return -E_ZERO_BYTES;
+
+ if (ret != sizeof(q))
+ return -E_PKT_SZ;
+
+ if (q.header != REPORT_REGISTER_BIT)
+ return -E_BAD_HEADER;
+
+ if (q.LRC != calculate_LRC((u8 *) & q, sizeof(q) - 1))
+ return -E_BAD_LRC;
+
+ *data = q.value_bit;
+
+ return 0;
+}
+
+static inline int
+write_reg(u8 offset, u8 value)
+{
+ static write_reg_t p;
+
+ p.header = WRITE_REGISTER;
+ p.offset = offset;
+ p.value = value;
+ p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+ return spi_xmt((u8 *) & p, sizeof(p));
+}
+
+static inline int
+read_reg(u8 offset, u8 * data)
+{
+ static read_reg_t p;
+ static report_reg_t q;
+ int ret;
+
+ p.header = READ_REGISTER;
+ p.offset = offset;
+ p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+ if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+ return ret;
+
+ if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0)
+ return ret;
+
+ if (ret == 0)
+ return -E_ZERO_BYTES;
+
+ if (ret != sizeof(q))
+ return -E_PKT_SZ;
+
+ if (q.header != REPORT_REGISTER)
+ return -E_BAD_HEADER;
+
+ if (q.LRC != calculate_LRC((u8 *) & q, sizeof(q) - 1))
+ return -E_BAD_LRC;
+
+ *data = q.value;
+
+ return 0;
+}
+
+static inline int
+write_block(u8 offset, u8 length, u8 * block)
+{
+ static write_block_t p;
+
+ p.header = WRITE_BLOCK;
+ p.offset = offset;
+ p.length = length;
+ memcpy(&p.block, block, length);
+ p.block[length] = calculate_LRC((u8 *) & p, 3 + length);
+
+ return spi_xmt((u8 *) & p, 4 + length);
+}
+
+static inline int
+read_block(u8 offset, u8 length, u8 * buf)
+{
+ static read_block_t p;
+ static report_block_t q;
+ int ret;
+
+ p.header = READ_BLOCK;
+ p.offset = offset;
+ p.length = length;
+ p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1);
+
+ if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0)
+ return ret;
+
+ if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0)
+ return ret;
+
+ if (ret == 0)
+ return -E_ZERO_BYTES;
+
+ if (ret != sizeof(4 + q.length))
+ return -E_PKT_SZ;
+
+ if (q.header != REPORT_BLOCK)
+ return -E_BAD_HEADER;
+
+ if (q.block[q.length] != calculate_LRC((u8 *) & q, 3 + q.length))
+ return -E_BAD_LRC;
+
+ if (length != q.length)
+ return -E_PKT_SZ;
+
+ memcpy(buf, &q.block, length);
+
+ return 0;
+}
+
+#ifdef INNOVATOR_KEYB_DEBUG
+static void
+ctrl_dump_regs(void)
+{
+ int i;
+ int n;
+
+ for (i = 0; i < 256; i += 8) {
+ read_block(i, 16, buffer);
+ mdelay(1);
+ }
+}
+#endif
+
+/*****************************************************************************/
+
+static void
+process_pointing_report(struct innovator_hid_dev *hid, u8 * buffer)
+{
+ static int prev_x, prev_y, prev_btn;
+ int x, y, btn;
+
+ if (buffer[1] & (1 << 3)) {
+ /* relative pointing device report */
+ x = buffer[2];
+ y = buffer[3];
+
+ /* check the sign and convert from 2's complement if negative */
+ if (buffer[1] & (1<<4))
+ x = ~(-x) - 255;
+
+ /* input driver wants -y */
+ if (buffer[1] & (1<<5))
+ y = -(~(-y) - 255);
+ else
+ y = -y;
+
+ input_report_key(&hid->mouse,
+ BTN_LEFT, buffer[1] & (1<<0));
+ input_report_key(&hid->mouse,
+ BTN_RIGHT, buffer[1] & (1<<1));
+ input_report_key(&hid->mouse,
+ BTN_MIDDLE, buffer[1] & (1<<2));
+ input_report_rel(&hid->mouse, REL_X, x);
+ input_report_rel(&hid->mouse, REL_Y, y);
+ } else {
+ /* REVISIT: Does this work? */
+ /* absolute pointing device report */
+ x = buffer[2] + ((buffer[1] & X_MSB_MASK) << X_MSB_SHIFT);
+ y = buffer[3] + ((buffer[1] & Y_MSB_MASK) << Y_MSB_SHIFT);
+ btn = buffer[1] & (1<<0);
+
+ if ((prev_x == x) && (prev_y == y)
+ && (prev_btn == btn))
+ return;
+
+ input_report_key(&hid->mouse, BTN_LEFT, btn);
+ input_report_abs(&hid->mouse, ABS_X, x);
+ input_report_abs(&hid->mouse, ABS_Y, y);
+ prev_x = x;
+ prev_y = y;
+ prev_btn = btn;
+ }
+ input_sync(&hid->mouse);
+ dbg("HID X: %d Y: %d Functions: %x\n", x, y, buffer[1]);
+}
+
+/*
+ * Reference [1], Appendix A, Semtech standard PS/2 key number definitions,
+ * pgs. A-1 through A-3. The following table lists standard PS/2 key numbers
+ * used by the Juno® 01 keyboard manager.
+ *
+ * NOTES:
+ * 1. The following table indices are E0 codes which require special handling:
+ * 53..62, 77..78, 94, 96, 100, 102..104, 108..110
+ * 2. The following table indices are E1 codes which require special handling:
+ * 101
+ */
+
+static unsigned char usar2scancode[128] = {
+ 0x00, 0x29, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x2b, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x1c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x39, 0x01, 0x52, 0x53, 0x4b,
+ 0x47, 0x4f, 0x48, 0x50, 0x49, 0x51, 0x4d, 0x37,
+ 0x4e, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
+ 0x48, 0x49, 0x52, 0x53, 0x4a, 0x1c, 0x35, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+ 0x44, 0x57, 0x58, 0x2a, 0x36, 0x38, 0x38, 0x1d,
+ 0x1d, 0x3a, 0x45, 0x46, 0x2a, 0x1d, 0x5b, 0x5c,
+ 0x5d, 0xff, 0x00, 0x00, 0x5e, 0x5f, 0x63, 0x70,
+ 0x7b, 0x79, 0x7d, 0x73, 0x5b, 0x5c, 0x5d, 0x63,
+ 0x65, 0x66, 0x68, 0x69, 0x6b, 0x56, 0x54, 0x00
+};
+
+/*
+ * The following are bit masks used to encode E0 scan codes which
+ * require special handling. However, scan codes 100 and 101 are
+ * excludable here since they each require unique multi-byte scan
+ * code translations and are therefore dealt with individually via
+ * handle_print_scr() and handle_pause() respectively below.
+ */
+
+static unsigned long int e0_codes1 = 0x030003ff; /* scan codes 53..84 */
+static unsigned long int e0_codes2 = 0x038e0a00; /* scan codes 85..116 */
+
+static void
+handle_print_scr(int up)
+{
+ if (up) {
+ input_report_key(&hid->keyboard, 0xe0, 1);
+ input_report_key(&hid->keyboard, 0xb7, 1);
+ input_report_key(&hid->keyboard, 0xe0, 1);
+ input_report_key(&hid->keyboard, 0xaa, 1);
+ } else {
+ input_report_key(&hid->keyboard, 0xe0, 0);
+ input_report_key(&hid->keyboard, 0x2a, 0);
+ input_report_key(&hid->keyboard, 0xe0, 0);
+ input_report_key(&hid->keyboard, 0x37, 0);
+ }
+}
+
+static void
+handle_pause(void)
+{
+ input_report_key(&hid->keyboard, 0xe1, 0);
+ input_report_key(&hid->keyboard, 0x1d, 0);
+ input_report_key(&hid->keyboard, 0x45, 0);
+ input_report_key(&hid->keyboard, 0xe1, 0);
+ input_report_key(&hid->keyboard, 0x9d, 0);
+ input_report_key(&hid->keyboard, 0xc5, 0);
+}
+
+static void
+process_keyboard_report(struct innovator_hid_dev *hid, u8 * buffer)
+{
+ unsigned char ch = buffer[1] & 0x7f;
+ int up = buffer[1] & 0x80 ? 1 : 0;
+ int is_e0 = 0;
+
+ if ((ch == 106) || (ch == 107))
+ return; /* no code */
+
+ if (ch == 100) {
+ handle_print_scr(up);
+ return;
+ }
+
+ if (ch == 101) {
+ handle_pause();
+ return;
+ }
+
+ if ((ch >= 53) && (ch <= 84)) {
+ /* first block of e0 codes */
+ is_e0 = e0_codes1 & (1 << (ch - 53));
+ } else if ((ch >= 85) && (ch <= 116)) {
+ /* second block of e0 codes */
+ is_e0 = e0_codes2 & (1 << (ch - 85));
+ }
+
+ if (is_e0) {
+ input_report_key(&hid->keyboard, 0xe0, !up);
+ }
+ input_report_key(&hid->keyboard, usar2scancode[ch], !up);
+ input_sync(&hid->keyboard);
+}
+
+static irqreturn_t
+innovator_hid_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (ATN()) {
+ disable_irq(OMAP1510_INT_FPGA_ATN);
+ tasklet_schedule(&hid_tasklet);
+ }
+ return IRQ_HANDLED;
+}
+
+static void
+do_hid_tasklet(unsigned long unused)
+{
+ int ret;
+ if ((ret = report_async(buffer, 256)) == -1) {
+ dbg("Error: Bad Juno return value: %d\n", ret);
+ } else if (ret == KEYBOARD_REPORT) {
+ process_keyboard_report(hid, buffer);
+ } else if (ret == POINTING_REPORT) {
+ process_pointing_report(hid, buffer);
+ } else {
+ dbg("ERROR: bad report\n");
+ }
+ enable_irq(OMAP1510_INT_FPGA_ATN);
+}
+
+static int
+innovator_hid_open(struct input_dev *dev)
+{
+ if (hid->open++)
+ return 0;
+
+ if (request_irq(OMAP1510_INT_FPGA_ATN, (void *) innovator_hid_interrupt,
+ SA_INTERRUPT, PFX, hid) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void
+innovator_hid_close(struct input_dev *dev)
+{
+ if (!--hid->open)
+ return;
+
+ if (hid == NULL)
+ return;
+
+ kfree(hid);
+}
+
+static int innovator_ps2_remove(struct device *dev)
+{
+ return 0;
+}
+
+static void innovator_ps2_device_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+static int innovator_ps2_suspend(struct device *dev, u32 state, u32 level)
+{
+ u8 pmcomm;
+
+
+ switch(level) {
+ case SUSPEND_DISABLE:
+
+ /*
+ * Set SUS_STATE in REG_PM_COMM (Page 0 R0). This will cause
+ * PM_MOD bits of REG_PM_STATUS to show suspended state,
+ * but the SUS_STAT bit of REG_PM_STATUS will continue to
+ * reflect the state of the _HSUS pin.
+ */
+
+ if (write_reg(REG_PAGENO, 0) < 0)
+ printk("ps2 suspend: write_reg REG_PAGENO error\n");
+
+ if (read_reg(REG_PM_COMM, &pmcomm) < 0)
+ printk("ps2 suspend: read_reg REG_PM_COMM error\n");
+
+ if (write_reg(REG_PM_COMM, pmcomm | SUS_STATE) < 0)
+ printk("ps2 suspend: write_reg REG_PM_COMM error\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int innovator_ps2_resume(struct device *dev, u32 level)
+{
+ u8 pmcomm;
+
+ switch(level) {
+ case RESUME_ENABLE:
+
+ /*
+ * Clear SUS_STATE from REG_PM_COMM (Page 0 R0).
+ */
+
+ if (write_reg(REG_PAGENO, 0) < 0)
+ printk("ps2 resume: write_reg REG_PAGENO error\n");
+
+ if (read_reg(REG_PM_COMM, &pmcomm) < 0)
+ printk("ps2 resume: read_reg REG_PM_COMM error\n");
+
+ if (write_reg(REG_PM_COMM, pmcomm & ~SUS_STATE) < 0)
+ printk("ps2 resume: write_reg REG_PM_COMM error\n");
+
+ break;
+ }
+
+ return 0;
+}
+
+static struct device_driver innovator_ps2_driver = {
+ .name = "innovator_ps2",
+ .bus = &platform_bus_type,
+ .remove = innovator_ps2_remove,
+ .suspend = innovator_ps2_suspend,
+ .resume = innovator_ps2_resume,
+};
+
+static struct platform_device innovator_ps2_device = {
+ .name = "ps2",
+ .id = -1,
+ .dev = {
+ .driver = &innovator_ps2_driver,
+ .release = innovator_ps2_device_release,
+ },
+};
+
+static int __init
+innovator_kbd_init(void)
+{
+ int i;
+ info("Innovator PS/2 keyboard/mouse driver v1.0\n");
+
+ innovator_fpga_hid_reset();
+
+ if ((hid = kmalloc(sizeof(struct innovator_hid_dev),
+ GFP_KERNEL)) == NULL) {
+ warn("unable to allocate space for HID device\n");
+ return -ENOMEM;
+ }
+
+ /* setup the mouse */
+ memset(hid, 0, sizeof(struct innovator_hid_dev));
+ hid->mouse.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ hid->mouse.keybit[LONG(BTN_MOUSE)] =
+ BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
+ BIT(BTN_MIDDLE) | BIT(BTN_TOUCH);
+ hid->mouse.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ hid->mouse.private = hid;
+ hid->mouse.open = innovator_hid_open;
+ hid->mouse.close = innovator_hid_close;
+ hid->mouse.name = "innovator_mouse";
+ hid->mouse.id.bustype = 0;
+ hid->mouse.id.vendor = 0;
+ hid->mouse.id.product = 0;
+ hid->mouse.id.version = 0;
+ hid->keyboard.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ init_input_dev(&hid->keyboard);
+ hid->keyboard.keycodesize = sizeof(unsigned char);
+ hid->keyboard.keycodemax = ARRAY_SIZE(usar2scancode);
+ for(i = 0; i < 128; i++)
+ set_bit(usar2scancode[i], hid->keyboard.keybit);
+ hid->keyboard.private = hid;
+ hid->keyboard.open = innovator_hid_open;
+ hid->keyboard.close = innovator_hid_close;
+ hid->keyboard.name = "innovator_keyboard";
+ hid->keyboard.id.bustype = 0;
+ hid->keyboard.id.vendor = 0;
+ hid->keyboard.id.product = 0;
+ hid->keyboard.id.version = 0;
+ input_register_device(&hid->mouse);
+ input_register_device(&hid->keyboard);
+ innovator_hid_open(&hid->mouse);
+ innovator_hid_open(&hid->keyboard);
+
+ if (driver_register(&innovator_ps2_driver) != 0)
+ printk(KERN_ERR "Driver register failed for innovator_ps2\n");
+
+ if (platform_device_register(&innovator_ps2_device) != 0) {
+ printk(KERN_ERR "Device register failed for ps2\n");
+ driver_unregister(&innovator_ps2_driver);
+ }
+
+#ifdef INNOVATOR_KEYB_DEBUG
+ ctrl_dump_regs();
+#endif
+ return 0;
+}
+
+static void __exit
+innovator_kbd_exit(void)
+{
+ input_unregister_device(&hid->mouse);
+ input_unregister_device(&hid->keyboard);
+ free_irq(OMAP1510_INT_FPGA_ATN, hid);
+ if (hid != NULL)
+ kfree(hid);
+ driver_unregister(&innovator_ps2_driver);
+ platform_device_unregister(&innovator_ps2_device);
+ return;
+}
+
+module_init(innovator_kbd_init);
+module_exit(innovator_kbd_exit);
+
+MODULE_AUTHOR("George G. Davis <gdavis@mvista.com>");
+MODULE_DESCRIPTION("Innovator PS/2 Driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/drivers/char/omap-keypad.c
+ *
+ * OMAP Keypad Driver
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/hardware.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/mach-types.h>
+#include <asm/arch/mux.h>
+
+#undef NEW_BOARD_LEARNING_MODE
+
+static void omap_kp_tasklet(unsigned long);
+static void omap_kp_timer(unsigned long);
+
+static struct input_dev omap_kp_dev;
+static unsigned char keypad_state[8];
+
+static struct timer_list kp_timer;
+DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+#define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
+
+static int h2_keymap[] = {
+ KEY(0, 0, KEY_LEFT),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_3),
+ KEY(0, 3, KEY_F10),
+ KEY(0, 4, KEY_F5),
+ KEY(0, 5, KEY_9),
+ KEY(1, 0, KEY_DOWN),
+ KEY(1, 1, KEY_UP),
+ KEY(1, 2, KEY_2),
+ KEY(1, 3, KEY_F9),
+ KEY(1, 4, KEY_F7),
+ KEY(1, 5, KEY_0),
+ KEY(2, 0, KEY_ENTER),
+ KEY(2, 1, KEY_6),
+ KEY(2, 2, KEY_1),
+ KEY(2, 3, KEY_F2),
+ KEY(2, 4, KEY_F6),
+ KEY(2, 5, KEY_HOME),
+ KEY(3, 0, KEY_8),
+ KEY(3, 1, KEY_5),
+ KEY(3, 2, KEY_F12),
+ KEY(3, 3, KEY_F3),
+ KEY(3, 4, KEY_F8),
+ KEY(3, 5, KEY_END),
+ KEY(4, 0, KEY_7),
+ KEY(4, 1, KEY_4),
+ KEY(4, 2, KEY_F11),
+ KEY(4, 3, KEY_F1),
+ KEY(4, 4, KEY_F4),
+ KEY(4, 5, KEY_ESC),
+ KEY(5, 0, KEY_F13),
+ KEY(5, 1, KEY_F14),
+ KEY(5, 2, KEY_F15),
+ KEY(5, 3, KEY_F16),
+ KEY(5, 4, KEY_SLEEP),
+ 0
+};
+
+static int test_keymap[] = {
+ KEY(0, 0, KEY_F4),
+ KEY(1, 0, KEY_LEFT),
+ KEY(2, 0, KEY_F1),
+ KEY(0, 1, KEY_DOWN),
+ KEY(1, 1, KEY_ENTER),
+ KEY(2, 1, KEY_UP),
+ KEY(0, 2, KEY_F3),
+ KEY(1, 2, KEY_RIGHT),
+ KEY(2, 2, KEY_F2),
+ 0
+};
+
+static int innovator_keymap[] = {
+ KEY(0, 0, KEY_F1),
+ KEY(0, 3, KEY_DOWN),
+ KEY(1, 1, KEY_F2),
+ KEY(1, 2, KEY_RIGHT),
+ KEY(2, 0, KEY_F3),
+ KEY(2, 1, KEY_F4),
+ KEY(2, 2, KEY_UP),
+ KEY(3, 2, KEY_ENTER),
+ KEY(3, 3, KEY_LEFT),
+ 0
+};
+
+static int osk_keymap[] = {
+ KEY(0, 0, KEY_F1),
+ KEY(0, 3, KEY_UP),
+ KEY(1, 1, KEY_LEFTCTRL),
+ KEY(1, 2, KEY_LEFT),
+ KEY(2, 0, KEY_SPACE),
+ KEY(2, 1, KEY_ESC),
+ KEY(2, 2, KEY_DOWN),
+ KEY(3, 2, KEY_ENTER),
+ KEY(3, 3, KEY_RIGHT),
+ 0
+};
+
+static int *keymap;
+
+static irqreturn_t omap_kp_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ tasklet_schedule(&kp_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void omap_kp_timer(unsigned long data)
+{
+ tasklet_schedule(&kp_tasklet);
+}
+
+static void omap_kp_scan_keypad(unsigned char *state)
+{
+ int col = 0;
+
+ /* read the keypad status */
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ for (col = 0; col < 8; col++) {
+ omap_writew(~(1 << col) & 0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+ if (machine_is_omap_osk() || machine_is_omap_h2() || machine_is_omap_h3()) {
+ udelay(9);
+ } else {
+ udelay(2);
+ }
+
+ state[col] = ~omap_readw(OMAP_MPUIO_BASE + OMAP_MPUIO_KBR_LATCH) & 0xff;
+ }
+ omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ udelay(2);
+}
+
+static inline int omap_kp_find_key(int col, int row)
+{
+ int i, key;
+
+ key = KEY(col, row, 0);
+ for (i = 0; keymap[i] != 0; i++)
+ if ((keymap[i] & 0xff000000) == key)
+ return keymap[i] & 0x00ffffff;
+ return -1;
+}
+
+static void omap_kp_tasklet(unsigned long data)
+{
+ unsigned char new_state[8], changed, key_down = 0;
+ int col, row;
+ int spurious = 0;
+
+ /* check for any changes */
+ omap_kp_scan_keypad(new_state);
+
+ /* check for changes and print those */
+ for (col = 0; col < 8; col++) {
+ changed = new_state[col] ^ keypad_state[col];
+ key_down |= new_state[col];
+ if (changed == 0)
+ continue;
+
+ for (row = 0; row < 8; row++) {
+ int key;
+ if (!(changed & (1 << row)))
+ continue;
+#ifdef NEW_BOARD_LEARNING_MODE
+ printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col, row, (new_state[col] & (1 << row)) ? "pressed" : "released");
+#else
+ key = omap_kp_find_key(col, row);
+ if (key < 0) {
+ printk(KERN_WARNING "omap-keypad: Spurious key event %d-%d\n",
+ col, row);
+ /* We scan again after a couple of seconds */
+ spurious = 1;
+ continue;
+ }
+
+ input_report_key(&omap_kp_dev, key,
+ new_state[col] & (1 << row));
+#endif
+ }
+ }
+ memcpy(keypad_state, new_state, sizeof(keypad_state));
+
+ if (key_down) {
+ int delay = HZ / 20;
+ /* some key is pressed - keep irq disabled and use timer
+ * to poll the keypad */
+ if (spurious)
+ delay = 2 * HZ;
+ mod_timer(&kp_timer, jiffies + delay);
+ } else {
+ /* enable interrupts */
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ }
+}
+
+static int __init omap_kp_init(void)
+{
+ int i;
+
+ printk(KERN_INFO "OMAP Keypad Driver\n");
+
+ /* Disable the interrupt for the MPUIO keyboard */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ if (machine_is_omap_h2() || machine_is_omap_h3()) {
+ keymap = h2_keymap;
+ set_bit(EV_REP, omap_kp_dev.evbit);
+ } else if (machine_is_omap_innovator()) {
+ keymap = innovator_keymap;
+ } else if (machine_is_omap_osk()) {
+ keymap = osk_keymap;
+ } else {
+ keymap = test_keymap;
+ }
+
+ init_timer(&kp_timer);
+ kp_timer.function = omap_kp_timer;
+
+ /* get the irq and init timer*/
+ tasklet_enable(&kp_tasklet);
+ if (request_irq(INT_KEYBOARD, omap_kp_interrupt, 0,
+ "omap-keypad", 0) < 0)
+ return -EINVAL;
+
+ /* setup input device */
+ set_bit(EV_KEY, omap_kp_dev.evbit);
+ for (i = 0; keymap[i] != 0; i++)
+ set_bit(keymap[i] & 0x00ffffff, omap_kp_dev.keybit);
+ omap_kp_dev.name = "omap-keypad";
+ input_register_device(&omap_kp_dev);
+
+ if (machine_is_omap_h2() || machine_is_omap_h3()) {
+ omap_cfg_reg(F18_1610_KBC0);
+ omap_cfg_reg(D20_1610_KBC1);
+ omap_cfg_reg(D19_1610_KBC2);
+ omap_cfg_reg(E18_1610_KBC3);
+ omap_cfg_reg(C21_1610_KBC4);
+
+ omap_cfg_reg(G18_1610_KBR0);
+ omap_cfg_reg(F19_1610_KBR1);
+ omap_cfg_reg(H14_1610_KBR2);
+ omap_cfg_reg(E20_1610_KBR3);
+ omap_cfg_reg(E19_1610_KBR4);
+ omap_cfg_reg(N19_1610_KBR5);
+
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+ }
+
+ /* scan current status and enable interrupt */
+ omap_kp_scan_keypad(keypad_state);
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ return 0;
+}
+
+static void __exit omap_kp_exit(void)
+{
+ /* disable keypad interrupt handling */
+ tasklet_disable(&kp_tasklet);
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ free_irq(INT_KEYBOARD, 0);
+ del_timer_sync(&kp_timer);
+
+ /* unregister everything */
+ input_unregister_device(&omap_kp_dev);
+}
+
+module_init(omap_kp_init);
+module_exit(omap_kp_exit);
+
+MODULE_AUTHOR("Timo Teräs");
+MODULE_DESCRIPTION("OMAP Keypad Driver");
+MODULE_LICENSE("GPL");
module will be called hp680_ts_input.
endif
+config TOUCHSCREEN_OMAP
+ tristate "OMAP touchscreen input driver"
+ depends on INPUT && INPUT_TOUCHSCREEN
+ help
+ Say Y here if you have an OMAP based board with touchscreen
+ attached to it, e.g. OMAP Innovator, OSK, H2 or H3
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap_ts.
+
+
+
#
-# Makefile for the mouse drivers.
+# Makefile for the touchscreen input drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_OMAP) += omap/
--- /dev/null
+#
+# Makefile for the OMAP touchscreen input driver
+#
+
+obj-$(CONFIG_TOUCHSCREEN_OMAP) += omapts.o
+
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H2) += ts_hx.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H3) += ts_hx.o
+objs-$(CONFIG_ARCH_OMAP1510)$(CONFIG_MACH_OMAP_INNOVATOR) += ts_inn1510.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_OSK) += ts_osk.o
+
+omapts-objs := omap_ts.o $(objs-yy)
--- /dev/null
+/*
+ * ads7846.h - header file for ADS7846 touchscreen controller
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * stevel@mvista.com or source@mvista.com
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ADS7846_H
+#define __ADS7846_H
+
+// ADS7846 Control Byte bit defines
+#define ADS7846_S (1<<7)
+#define ADS7846_ADDR_BIT 4
+#define ADS7846_ADDR_MASK (0x7<<ADS7846_ADDR_BIT)
+#define ADS7846_MEASURE_X (0x5<<ADS7846_ADDR_BIT)
+#define ADS7846_MEASURE_Y (0x1<<ADS7846_ADDR_BIT)
+#define ADS7846_MEASURE_Z1 (0x3<<ADS7846_ADDR_BIT)
+#define ADS7846_MEASURE_Z2 (0x4<<ADS7846_ADDR_BIT)
+#define ADS7846_8BITS (1<<3)
+#define ADS7846_12BITS 0
+#define ADS7846_SER (1<<2)
+#define ADS7846_DFR 0
+#define ADS7846_PWR_BIT 0
+#define ADS7846_PD 0
+#define ADS7846_ADC_ON (0x1<<ADS7846_PWR_BIT)
+#define ADS7846_REF_ON (0x2<<ADS7846_PWR_BIT)
+#define ADS7846_REF_ADC_ON (0x3<<ADS7846_PWR_BIT)
+
+#define MEASURE_12BIT_X \
+ (ADS7846_S | ADS7846_MEASURE_X | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+#define MEASURE_12BIT_Y \
+ (ADS7846_S | ADS7846_MEASURE_Y | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+#define MEASURE_12BIT_Z1 \
+ (ADS7846_S | ADS7846_MEASURE_Z1 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+#define MEASURE_12BIT_Z2 \
+ (ADS7846_S | ADS7846_MEASURE_Z2 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
+
+#endif /* __ADS7846_H */
--- /dev/null
+/*
+ * input/touchscreen/omap/omap_ts.c
+ *
+ * touchscreen input device driver for various TI OMAP boards
+ * Copyright (c) 2002 MontaVista Software Inc.
+ * Copyright (c) 2004 Texas Instruments, Inc.
+ * Cleanup and modularization 2004 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * Assembled using driver code copyright the companies above.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * History:
+ * 12/12/2004 Srinath Modified and intergrated code for H2 and H3
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/device.h>
+
+#include <asm/mach-types.h>
+
+//#define DEBUG
+
+#include "omap_ts.h"
+
+#define OMAP_TS_NAME "omap_ts"
+
+static struct ts_device *__initdata ts_devs[] = {
+#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
+ &hx_ts,
+#endif
+#ifdef CONFIG_MACH_OMAP_OSK
+ &osk_ts,
+#endif
+#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP1510)
+ &innovator1510_ts,
+#endif
+};
+
+static struct omap_ts_t ts_omap;
+
+static int omap_ts_read(void)
+{
+ u16 data[4] = { 0, 0, 0, 0 };
+
+ ts_omap.dev->read(data);
+
+ input_report_abs(&(ts_omap.inputdevice), ABS_X, data[0]);
+ input_report_abs(&(ts_omap.inputdevice), ABS_Y, data[1]);
+ input_report_abs(&(ts_omap.inputdevice), ABS_PRESSURE, data[2]);
+ input_sync(&(ts_omap.inputdevice));
+
+ DEBUG_TS("omap_ts_read: read x=%d,y=%d,p=%d\n", data[0], data[1],
+ data[2]);
+
+ return 0;
+}
+
+static void omap_ts_timer(unsigned long data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts_omap.lock, flags);
+
+ if (!ts_omap.dev->penup()) {
+ if (!ts_omap.touched) {
+ DEBUG_TS("omap_ts_timer: pen down\n");
+ input_report_key(&(ts_omap.inputdevice), BTN_TOUCH, 1);
+ }
+ ts_omap.touched = 1;
+ omap_ts_read();
+ ts_omap.ts_timer.expires = jiffies + HZ / 100;
+ add_timer(&(ts_omap.ts_timer));
+ } else {
+ if (ts_omap.touched) {
+ DEBUG_TS("omap_ts_timer: pen up\n");
+ ts_omap.touched = 0;
+ input_report_abs(&(ts_omap.inputdevice), ABS_X, 0);
+ input_report_abs(&(ts_omap.inputdevice), ABS_Y, 0);
+ input_report_abs(&(ts_omap.inputdevice), ABS_PRESSURE,
+ 0);
+ input_sync(&(ts_omap.inputdevice));
+ input_report_key(&(ts_omap.inputdevice), BTN_TOUCH, 0);
+ }
+ if (!ts_omap.irq_enabled) {
+ ts_omap.irq_enabled = 1;
+ enable_irq(ts_omap.irq);
+ }
+ }
+
+ spin_unlock_irqrestore(&ts_omap.lock, flags);
+}
+
+static irqreturn_t omap_ts_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ spin_lock(&ts_omap.lock);
+
+ if (ts_omap.irq_enabled) {
+ ts_omap.irq_enabled = 0;
+ disable_irq(irq);
+ }
+ // restart acquire
+ ts_omap.ts_timer.expires = jiffies + HZ / 100;
+ add_timer(&(ts_omap.ts_timer));
+
+ spin_unlock(&ts_omap.lock);
+
+ return IRQ_HANDLED;
+}
+
+static int __init omap_ts_probe(struct device *dev)
+{
+ int i;
+ int status = -ENODEV;
+
+ memset(&ts_omap, 0, sizeof(ts_omap));
+ spin_lock_init(&ts_omap.lock);
+
+ for (i = 0; i < ARRAY_SIZE(ts_devs); i++) {
+ if (!ts_devs[i] || !ts_devs[i]->probe)
+ continue;
+ status = ts_devs[i]->probe(&ts_omap);
+ if (status == 0) {
+ ts_omap.dev = ts_devs[i];
+ break;
+ }
+ }
+
+ if (status != 0)
+ return status;
+
+ // Init acquisition timer function
+ init_timer(&ts_omap.ts_timer);
+ ts_omap.ts_timer.function = omap_ts_timer;
+
+ /* request irq */
+ if (ts_omap.irq != -1) {
+ if (request_irq(ts_omap.irq, omap_ts_handler, 0,
+ OMAP_TS_NAME, &ts_omap)) {
+ printk(KERN_ERR
+ "omap_ts.c: Could not allocate touchscreen IRQ!\n");
+ ts_omap.irq = -1;
+ return -EINVAL;
+ }
+ ts_omap.irq_enabled = 1;
+ } else {
+ printk(KERN_ERR "omap_ts.c: No touchscreen IRQ assigned!\n");
+ return -EINVAL;
+ }
+
+ init_input_dev(&(ts_omap.inputdevice));
+ ts_omap.inputdevice.name = OMAP_TS_NAME;
+ ts_omap.inputdevice.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ ts_omap.inputdevice.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+ ts_omap.inputdevice.absbit[0] =
+ BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+ input_register_device(&(ts_omap.inputdevice));
+
+ ts_omap.dev->enable();
+
+ printk("OMAP touchscreen driver initialized\n");
+
+ return 0;
+}
+
+static int __exit omap_ts_remove(struct device *dev)
+{
+ ts_omap.dev->disable();
+ input_unregister_device(&ts_omap.inputdevice);
+ if (ts_omap.irq != -1)
+ free_irq(ts_omap.irq, &ts_omap);
+
+ ts_omap.dev->remove();
+
+ return 0;
+}
+
+static int omap_ts_suspend(struct device *dev, pm_message_t mesg, u32 level)
+{
+ if (level != SUSPEND_POWER_DOWN) {
+ return 0;
+ }
+
+ ts_omap.dev->disable();
+ return 0;
+}
+
+static int omap_ts_resume(struct device *dev, u32 level)
+{
+ if (level != RESUME_POWER_ON) {
+ return 0;
+ }
+
+ ts_omap.dev->enable();
+ return 0;
+}
+
+static void omap_ts_device_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+static struct device_driver omap_ts_driver = {
+ .name = OMAP_TS_NAME,
+ .bus = &platform_bus_type,
+ .probe = omap_ts_probe,
+ .remove = __exit_p(omap_ts_remove),
+ .suspend = omap_ts_suspend,
+ .resume = omap_ts_resume,
+};
+
+static struct platform_device omap_ts_device = {
+ .name = OMAP_TS_NAME,
+ .id = -1,
+ .dev = {
+ .release = omap_ts_device_release,
+ },
+};
+
+static int __init omap_ts_init(void)
+{
+ int ret;
+
+ ret = platform_device_register(&omap_ts_device);
+ if (ret != 0)
+ return -ENODEV;
+
+ ret = driver_register(&omap_ts_driver);
+ if (ret != 0) {
+ platform_device_unregister(&omap_ts_device);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit omap_ts_exit(void)
+{
+ driver_unregister(&omap_ts_driver);
+ platform_device_unregister(&omap_ts_device);
+}
+
+module_init(omap_ts_init);
+module_exit(omap_ts_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * omap_ts.h - header file for OMAP touchscreen support
+ *
+ * Copyright (c) 2002 MontaVista Software Inc.
+ * Copyright (c) 2004 Texas Instruments, Inc.
+ *
+ * Assembled using driver code copyright the companies above.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __OMAP_TS_H
+#define __OMAP_TS_H
+
+#ifdef DEBUG
+#define DEBUG_TS(fmt...) printk(fmt)
+#else
+#define DEBUG_TS(fmt...) do { } while (0)
+#endif
+
+struct omap_ts_t;
+
+struct ts_device {
+ int (*probe) (struct omap_ts_t *);
+ void (*read) (u16 *);
+ void (*enable) (void);
+ void (*disable) (void);
+ void (*remove) (void);
+ int (*penup) (void);
+};
+
+struct omap_ts_t{
+ struct input_dev inputdevice;
+ struct timer_list ts_timer; // Timer for triggering acquisitions
+ int touched;
+ int irq;
+ int irq_enabled;
+ struct ts_device *dev;
+ spinlock_t lock;
+};
+
+extern struct ts_device hx_ts;
+extern struct ts_device osk_ts;
+extern struct ts_device innovator1510_ts;
+
+#endif /* __OMAP_TS_H */
--- /dev/null
+/*
+ * input/touchscreen/omap/ts_hx.c
+ * touchscreen support for OMAP H3 and H2 boards
+ *
+ * Copyright (c) 2002 MontaVista Software Inc.
+ * Copyright (c) 2004 Texas Instruments, Inc.
+ *
+ * Assembled using driver code copyright the companies above.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * History:
+ * 9/12/2004 Srinath Modified and integrated H2 and H3 code
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <asm/mach-types.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/hardware.h>
+#include <asm/hardware/tsc2101.h>
+
+#include "../drivers/ssi/omap-tsc2101.h"
+#include "omap_ts.h"
+
+#define H2_GPIO_NUM 4
+#define H3_GPIO_NUM 48
+
+#define OMAP_TSC2101_XRES 500
+#define TOUCHSCREEN_DATA_REGISTERS_PAGE 0x0
+#define TOUCHSCREEN_CONTROL_REGISTERS_PAGE 0x1
+#define OMAP_TSC2101_READ_MAX 0x4
+#define TSC2101_GETSTATUS(ret) (((ret) >> 11) & 0x1)
+#define TSC2101_MASKVAL 0xFFF
+#define TSC2101_PRESSUREVAL(x) ((x) << 12)
+
+static int hx_ts_penup(void);
+static int hx_ts_probe(struct omap_ts_t *ts);
+static void hx_ts_read(u16 * data);
+static void hx_ts_enable(void);
+static void hx_ts_disable(void);
+#ifdef MODULE
+static void hx_ts_remove(void);
+#endif
+
+struct ts_device hx_ts = {
+ .probe = hx_ts_probe,
+ .read = hx_ts_read,
+ .enable = hx_ts_enable,
+ .disable = hx_ts_disable,
+ .remove = __exit_p(hx_ts_remove),
+ .penup = hx_ts_penup,
+};
+
+static int hx_ts_penup(void)
+{
+ int ret = 0;
+ /* Read the status register */
+ ret = omap_tsc2101_read(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_STATUS);
+ /* Check for availability of data in status register */
+ ret = TSC2101_GETSTATUS(ret);
+ return !ret;
+
+}
+
+static int __init hx_ts_probe(struct omap_ts_t *ts)
+{
+ unsigned gpio;
+
+ if (machine_is_omap_h2()) {
+ gpio = H2_GPIO_NUM;
+ omap_cfg_reg(P20_1610_GPIO4);
+ } else if (machine_is_omap_h3()) {
+ gpio = H3_GPIO_NUM;
+ omap_cfg_reg(W19_1610_GPIO48);
+ } else
+ return -ENODEV;
+
+ ts->irq = OMAP_GPIO_IRQ(gpio);
+ if (omap_request_gpio(gpio) != 0) {
+ printk(KERN_ERR "hX_ts_init.c: Could not reserve GPIO!\n");
+ return -EINVAL;
+ };
+
+ omap_set_gpio_direction(gpio, 1);
+ omap_set_gpio_edge_ctrl(gpio, OMAP_GPIO_FALLING_EDGE);
+ return 0;
+}
+
+static void hx_ts_read(u16 * values)
+{
+ s32 t, p = 0;
+ int i;
+
+ /* Read X, Y, Z1 and Z2 */
+ omap_tsc2101_reads(TOUCHSCREEN_DATA_REGISTERS_PAGE, TSC2101_TS_X,
+ values, OMAP_TSC2101_READ_MAX);
+
+ for (i = 0; i < OMAP_TSC2101_READ_MAX; i++)
+ values[i] &= TSC2101_MASKVAL;
+
+ /* Calculate Pressure */
+ if (values[TSC2101_TS_Z1] != 0) {
+ t = ((OMAP_TSC2101_XRES * values[TSC2101_TS_X]) *
+ (values[TSC2101_TS_Z2] - values[TSC2101_TS_Z1]));
+ p = t / (u32) (TSC2101_PRESSUREVAL(values[TSC2101_TS_Z1]));
+ if (p < 0)
+ p = 0;
+ }
+
+ values[TSC2101_TS_Z1] = p;
+}
+
+static void hx_ts_enable(void)
+{
+ int ret = omap_tsc2101_enable();
+ if (ret) {
+ printk(KERN_ERR "FAILED TO INITIALIZE TSC CODEC\n");
+ return;
+ }
+
+ /* PINTDAV is data available only */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_STATUS, TSC2101_DATA_AVAILABLE);
+ /* disable buffer mode */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_BUFFER_CTRL, TSC2101_BUFFERMODE_DISABLE);
+ /* use internal reference, 100 usec power-up delay,
+ * * power down between conversions, 1.25V internal reference */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_REF_CTRL, TSC2101_REF_POWERUP);
+ /* enable touch detection, 84usec precharge time, 32 usec sense time */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_CONFIG_CTRL, TSC2101_ENABLE_TOUCHDETECT);
+ /* 3 msec conversion delays */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_PROG_DELAY, TSC2101_PRG_DELAY);
+ /*
+ * TSC2101-controlled conversions
+ * 12-bit samples
+ * continuous X,Y,Z1,Z2 scan mode
+ * average (mean) 4 samples per coordinate
+ * 1 MHz internal conversion clock
+ * 500 usec panel voltage stabilization delay
+ */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_ADC_CTRL, TSC2101_ADC_CONTROL);
+
+ return;
+
+}
+
+static void hx_ts_disable(void)
+{
+ /* stop conversions and power down */
+ omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE,
+ TSC2101_TS_ADC_CTRL, TSC2101_ADC_POWERDOWN);
+ omap_tsc2101_disable();
+}
+
+#ifdef MODULE
+static void __exit hx_ts_remove(void)
+{
+ if (machine_is_omap_h2())
+ omap_free_gpio(H2_GPIO_NUM);
+ else if (machine_is_omap_h3())
+ omap_free_gpio(H3_GPIO_NUM);
+}
+#endif
--- /dev/null
+/*
+ * ts_inn1510.c - touchscreen support for OMAP1510 Innovator board
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * stevel@mvista.com or source@mvista.com
+ *
+ * The touchscreen hardware on the Innovator consists of an FPGA
+ * register which is bit-banged to generate SPI-like transactions
+ * to an ADS7846 touch screen controller.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/arch/fpga.h>
+
+#include "omap_ts.h"
+#include "ads7846.h"
+
+// The Touch Screen Register on Innovator FPGA
+#define FPGA_TS_BCLK (1<<0)
+#define FPGA_TS_BDIN (1<<1)
+#define FPGA_TS_BCS (1<<2)
+#define FPGA_TS_BBUSY (1<<3)
+#define FPGA_TS_BOUT (1<<4)
+#define FPGA_TS_BPENUP (1<<5)
+
+#define X_PLATE_OHMS 419
+#define Y_PLATE_OHMS 486
+
+static int inn1510_ts_penup(void);
+static int inn1510_ts_probe(struct omap_ts_t *ts);
+static void inn1510_ts_read(u16 * data);
+static void inn1510_ts_enable(void);
+static void inn1510_ts_disable(void);
+#ifdef MODULE
+static void inn1510_ts_remove(void);
+#endif
+
+struct ts_device innovator1510_ts = {
+ .probe = inn1510_ts_probe,
+ .read = inn1510_ts_read,
+ .enable = inn1510_ts_enable,
+ .disable = inn1510_ts_disable,
+ .remove = __exit_p(inn1510_ts_remove),
+ .penup = inn1510_ts_penup,
+};
+
+static inline u8 fpga_ts_read(void)
+{
+ return fpga_read(OMAP1510_FPGA_TOUCHSCREEN);
+}
+
+static inline void fpga_ts_write(u8 val)
+{
+ fpga_write(val, OMAP1510_FPGA_TOUCHSCREEN);
+}
+
+static inline void fpga_ts_set_bits(u8 mask)
+{
+ fpga_ts_write(fpga_ts_read() | mask);
+}
+
+static inline void fpga_ts_clear_bits(u8 mask)
+{
+ fpga_ts_write(fpga_ts_read() & ~mask);
+}
+
+static inline void CS_H(void)
+{
+ // EPLD inverts active low signals.
+ fpga_ts_clear_bits(FPGA_TS_BCS);
+}
+
+static inline void CS_L(void)
+{
+ fpga_ts_set_bits(FPGA_TS_BCS);
+}
+
+static inline void SCLK_L(void)
+{
+ fpga_ts_clear_bits(FPGA_TS_BCLK);
+}
+
+static inline void SCLK_H(void)
+{
+ fpga_ts_set_bits(FPGA_TS_BCLK);
+}
+
+static inline void SDI_L(void)
+{
+ fpga_ts_clear_bits(FPGA_TS_BDIN);
+}
+
+static inline void SDI_H(void)
+{
+ fpga_ts_set_bits(FPGA_TS_BDIN);
+}
+
+static inline int BUSY(void)
+{
+ return (((fpga_ts_read() & FPGA_TS_BBUSY) == 0) ? 1 : 0) ;
+}
+
+static inline u8 DOUT(void)
+{
+ return ((fpga_ts_read() & FPGA_TS_BOUT) ? 1 : 0) ;
+}
+
+static u16 ads7846_do(u8 cmd)
+{
+ int i;
+ u16 val=0;
+
+ SCLK_L() ;
+ SDI_L();
+ CS_L() ; // enable the chip select
+
+ // send the command to the ADS7846
+ for (i=0; i<8; i++ ) {
+ if (cmd & 0x80)
+ SDI_H();
+ else
+ SDI_L(); // prepare the data on line sdi OR din
+
+ SCLK_H() ; // clk in the data
+ cmd <<= 1 ;
+ SCLK_L() ;
+ }
+
+ SDI_L();
+ while (BUSY())
+ ;
+
+ // now read returned data
+ for (i=0 ; i<16 ; i++ ) {
+ SCLK_L() ;
+
+ if (i < 12) {
+ val <<= 1 ;
+ val |= DOUT();
+ }
+ SCLK_H() ;
+ }
+
+ SCLK_L() ;
+ CS_H() ; // disable the chip select
+
+ return val;
+}
+
+static int inn1510_ts_penup(void)
+{
+ return ((fpga_ts_read() & FPGA_TS_BPENUP) ? 0 : 1) ;
+}
+
+static int __init inn1510_ts_probe(struct omap_ts_t *ts)
+{
+ if (!cpu_is_omap15xx() || !machine_is_omap_innovator())
+ return -ENODEV;
+
+ ts->irq = OMAP1510_INT_FPGA_TS;
+
+ return 0;
+}
+
+static void inn1510_ts_read(u16 *data)
+{
+ unsigned int Rt = 0;
+
+ data[0] = ads7846_do(MEASURE_12BIT_X);
+ data[1] = ads7846_do(MEASURE_12BIT_Y);
+ data[2] = ads7846_do(MEASURE_12BIT_Z1);
+ data[3] = ads7846_do(MEASURE_12BIT_Z2);
+
+ // Calculate touch pressure resistance
+ if (data[2]) {
+ Rt = (X_PLATE_OHMS * (u32)data[0] *
+ ((u32)data[3] - (u32)data[2])) / (u32)data[2];
+
+ Rt = (Rt + 2048) >> 12; // round up to nearest ohm
+ }
+
+ data[2] = Rt;
+}
+
+static void inn1510_ts_enable(void)
+{
+
+}
+
+static void inn1510_ts_disable(void)
+{
+
+}
+
+#ifdef MODULE
+static void __exit inn1510_ts_remove(void)
+{
+ /* Nothing to do here */
+}
+#endif
--- /dev/null
+/*
+ * ts_osk.c - touchscreen support for OMAP OSK board
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * stevel@mvista.com or source@mvista.com
+ *
+ * The touchscreen hardware on the OSK uses OMAP5912 uWire interface,
+ * GPIO4 (/PENIRQ) and GPIO6 (BUSY) to connect to an ADS7846
+ * touch screen controller. GPIO6 doesn't seem to be necessary here.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/gpio.h>
+#include <asm/mach-types.h>
+
+#include "../drivers/ssi/omap-uwire.h"
+
+#include "omap_ts.h"
+#include "ads7846.h"
+
+// /PENIRQ on GPIO4 on OSK
+#define PEN_IRQ OMAP_GPIO_IRQ(4)
+
+// ADS7846 is on OSK uWire CS 0
+#define ADS7846_UWIRE_CS 0
+#define UWIRE_LEAVE_CS 1
+
+#define X_PLATE_OHMS 419
+#define Y_PLATE_OHMS 486
+
+static int osk_ts_penup(void);
+static int osk_ts_probe(struct omap_ts_t *ts);
+static void osk_ts_read(u16 * data);
+static void osk_ts_enable(void);
+static void osk_ts_disable(void);
+#ifdef MODULE
+static void osk_ts_remove(void);
+#endif
+
+struct ts_device osk_ts = {
+ .probe = osk_ts_probe,
+ .read = osk_ts_read,
+ .enable = osk_ts_enable,
+ .disable = osk_ts_disable,
+ .remove = __exit_p(osk_ts_remove),
+ .penup = osk_ts_penup,
+};
+
+static u16 ads7846_do(u8 cmd)
+{
+ u16 val = 0;
+
+ // send the command to the ADS7846, leave CS active after this
+ omap_uwire_data_transfer(ADS7846_UWIRE_CS, cmd, 8, 0, NULL, UWIRE_LEAVE_CS);
+
+ // now read returned data
+ omap_uwire_data_transfer(ADS7846_UWIRE_CS, 0, 0, 16, &val, !UWIRE_LEAVE_CS);
+
+ return val;
+}
+
+static int osk_ts_penup(void)
+{
+ return (omap_get_gpio_datain(4));
+}
+
+static int __init osk_ts_probe(struct omap_ts_t *ts)
+{
+ if (!machine_is_omap_osk())
+ return -ENODEV;
+
+ /* Configure GPIO4 (pin M17 ZDY) as /PENIRQ interrupt input */
+ omap_cfg_reg(P20_1610_GPIO4);
+ omap_request_gpio(4);
+ omap_set_gpio_direction(4, 1);
+ omap_set_gpio_edge_ctrl(4, OMAP_GPIO_FALLING_EDGE);
+
+ ts->irq = PEN_IRQ;
+
+ /* Configure uWire interface. ADS7846 is on CS0 */
+ omap_uwire_configure_mode(ADS7846_UWIRE_CS, UWIRE_READ_RISING_EDGE |
+ UWIRE_WRITE_RISING_EDGE |
+ UWIRE_CS_ACTIVE_LOW |
+ UWIRE_FREQ_DIV_2);
+
+ return 0;
+}
+
+static void osk_ts_read(u16 *data)
+{
+ unsigned int Rt = 0;
+
+ data[0] = ads7846_do(MEASURE_12BIT_X);
+ data[1] = ads7846_do(MEASURE_12BIT_Y);
+ data[2] = ads7846_do(MEASURE_12BIT_Z1);
+ data[3] = ads7846_do(MEASURE_12BIT_Z2);
+
+ // Calculate touch pressure resistance
+ if (data[2]) {
+ Rt = (X_PLATE_OHMS * (u32)data[0] *
+ ((u32)data[3] - (u32)data[2])) / (u32)data[2];
+
+ Rt = (Rt + 2048) >> 12; // round up to nearest ohm
+ }
+
+ /*
+ * Raw OSK touchscreen data values are between ~4000 and
+ * ~60000. This seems to be to large for calibration
+ * systems (e.g. tslib). Make the values smaller.
+ */
+ data[0] = data[0] >> 4;
+ data[1] = data[1] >> 4;
+
+ data[2] = Rt;
+}
+
+static void osk_ts_enable(void)
+{
+
+}
+
+static void osk_ts_disable(void)
+{
+
+}
+
+#ifdef MODULE
+static void __exit osk_ts_remove(void)
+{
+ omap_free_gpio(4);
+}
+#endif