Commit 6425efa7 authored by Tony Lindgren's avatar Tony Lindgren

Add various OMAP input drivers

Adds various OMAP input drivers.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 2971216f
...@@ -183,3 +183,23 @@ config KEYBOARD_HIL ...@@ -183,3 +183,23 @@ config KEYBOARD_HIL
to your machine, so normally you should say Y here. to your machine, so normally you should say Y here.
endif 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.
...@@ -16,4 +16,5 @@ obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o ...@@ -16,4 +16,5 @@ obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o
obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_OMAP_PS2) += innovator_ps2.o
/*
* 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");
/*
* linux/drivers/char/omap-keypad.c
*
* OMAP Keypad Driver
*
* Copyright (C) 2003 Nokia Corporation
* Written by Timo Ters <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 Ters");
MODULE_DESCRIPTION("OMAP Keypad Driver");
MODULE_LICENSE("GPL");
...@@ -96,3 +96,17 @@ config TOUCHSCREEN_HP600 ...@@ -96,3 +96,17 @@ config TOUCHSCREEN_HP600
module will be called hp680_ts_input. module will be called hp680_ts_input.
endif 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. # Each configuration option enables a list of files.
...@@ -11,3 +11,4 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o ...@@ -11,3 +11,4 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_OMAP) += omap/
#
# 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)
/*
* 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 */
/*
* 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");
/*
* 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 */
/*
* 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
/*
* 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
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment