mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-09-08 15:58:28 +00:00
1. Get mem from platform resource in mxc keypad driver 2. Remove unnecessory header file Signed-off-by: Lily Zhang <r58066@freescale.com>
1203 lines
31 KiB
C
1203 lines
31 KiB
C
/*
|
|
* Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* The code contained herein is licensed under the GNU General Public
|
|
* License. You may obtain a copy of the GNU General Public License
|
|
* Version 2 or later at the following locations:
|
|
*
|
|
* http://www.opensource.org/licenses/gpl-license.html
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
/*!
|
|
* @file mxc_keyb.c
|
|
*
|
|
* @brief Driver for the Freescale Semiconductor MXC keypad port.
|
|
*
|
|
* The keypad driver is designed as a standard Input driver which interacts
|
|
* with low level keypad port hardware. Upon opening, the Keypad driver
|
|
* initializes the keypad port. When the keypad interrupt happens the driver
|
|
* calles keypad polling timer and scans the keypad matrix for key
|
|
* press/release. If all key press/release happened it comes out of timer and
|
|
* waits for key press interrupt. The scancode for key press and release events
|
|
* are passed to Input subsytem.
|
|
*
|
|
* @ingroup keypad
|
|
*/
|
|
|
|
/*!
|
|
* Comment KPP_DEBUG to disable debug messages
|
|
*/
|
|
#define KPP_DEBUG 0
|
|
|
|
#if KPP_DEBUG
|
|
#define DEBUG
|
|
#include <linux/kernel.h>
|
|
#endif
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/init.h>
|
|
#include <asm/io.h>
|
|
#include <asm/uaccess.h>
|
|
#include <mach/hardware.h>
|
|
#include <linux/kd.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/kbd_kern.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/input.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/clk.h>
|
|
#include <asm/mach/keypad.h>
|
|
|
|
/*!
|
|
* Keypad Module Name
|
|
*/
|
|
#define MOD_NAME "mxckpd"
|
|
|
|
/*!
|
|
* XLATE mode selection
|
|
*/
|
|
#define KEYPAD_XLATE 0
|
|
|
|
/*!
|
|
* RAW mode selection
|
|
*/
|
|
#define KEYPAD_RAW 1
|
|
|
|
/*!
|
|
* Maximum number of keys.
|
|
*/
|
|
#define MAXROW 8
|
|
#define MAXCOL 8
|
|
#define MXC_MAXKEY (MAXROW * MAXCOL)
|
|
|
|
/*!
|
|
* This define indicates break scancode for every key release. A constant
|
|
* of 128 is added to the key press scancode.
|
|
*/
|
|
#define MXC_KEYRELEASE 128
|
|
|
|
/*
|
|
* _reg_KPP_KPCR _reg_KPP_KPSR _reg_KPP_KDDR _reg_KPP_KPDR
|
|
* The offset of Keypad Control Register Address
|
|
*/
|
|
#define KPCR 0x00
|
|
|
|
/*
|
|
* The offset of Keypad Status Register Address
|
|
*/
|
|
#define KPSR 0x02
|
|
|
|
/*
|
|
* The offset of Keypad Data Direction Address
|
|
*/
|
|
#define KDDR 0x04
|
|
|
|
/*
|
|
* The offset of Keypad Data Register
|
|
*/
|
|
#define KPDR 0x06
|
|
|
|
/*
|
|
* Key Press Interrupt Status bit
|
|
*/
|
|
#define KBD_STAT_KPKD 0x01
|
|
|
|
/*
|
|
* Key Release Interrupt Status bit
|
|
*/
|
|
#define KBD_STAT_KPKR 0x02
|
|
|
|
/*
|
|
* Key Depress Synchronizer Chain Status bit
|
|
*/
|
|
#define KBD_STAT_KDSC 0x04
|
|
|
|
/*
|
|
* Key Release Synchronizer Status bit
|
|
*/
|
|
#define KBD_STAT_KRSS 0x08
|
|
|
|
/*
|
|
* Key Depress Interrupt Enable Status bit
|
|
*/
|
|
#define KBD_STAT_KDIE 0x100
|
|
|
|
/*
|
|
* Key Release Interrupt Enable
|
|
*/
|
|
#define KBD_STAT_KRIE 0x200
|
|
|
|
/*
|
|
* Keypad Clock Enable
|
|
*/
|
|
#define KBD_STAT_KPPEN 0x400
|
|
|
|
/*!
|
|
* Buffer size of keypad queue. Should be a power of 2.
|
|
*/
|
|
#define KPP_BUF_SIZE 128
|
|
|
|
/*!
|
|
* Test whether bit is set for integer c
|
|
*/
|
|
#define TEST_BIT(c, n) ((c) & (0x1 << (n)))
|
|
|
|
/*!
|
|
* Set nth bit in the integer c
|
|
*/
|
|
#define BITSET(c, n) ((c) | (1 << (n)))
|
|
|
|
/*!
|
|
* Reset nth bit in the integer c
|
|
*/
|
|
#define BITRESET(c, n) ((c) & ~(1 << (n)))
|
|
|
|
/*!
|
|
* This enum represents the keypad state machine to maintain debounce logic
|
|
* for key press/release.
|
|
*/
|
|
enum KeyState {
|
|
|
|
/*!
|
|
* Key press state.
|
|
*/
|
|
KStateUp,
|
|
|
|
/*!
|
|
* Key press debounce state.
|
|
*/
|
|
KStateFirstDown,
|
|
|
|
/*!
|
|
* Key release state.
|
|
*/
|
|
KStateDown,
|
|
|
|
/*!
|
|
* Key release debounce state.
|
|
*/
|
|
KStateFirstUp
|
|
};
|
|
|
|
/*!
|
|
* Keypad Private Data Structure
|
|
*/
|
|
struct keypad_priv {
|
|
|
|
/*!
|
|
* Keypad state machine.
|
|
*/
|
|
enum KeyState iKeyState;
|
|
|
|
/*!
|
|
* Number of rows configured in the keypad matrix
|
|
*/
|
|
unsigned long kpp_rows;
|
|
|
|
/*!
|
|
* Number of Columns configured in the keypad matrix
|
|
*/
|
|
unsigned long kpp_cols;
|
|
|
|
/*!
|
|
* Timer used for Keypad polling.
|
|
*/
|
|
struct timer_list poll_timer;
|
|
|
|
/*!
|
|
* The base address
|
|
*/
|
|
void __iomem *base;
|
|
};
|
|
/*!
|
|
* This structure holds the keypad private data structure.
|
|
*/
|
|
static struct keypad_priv kpp_dev;
|
|
|
|
/*! Indicates if the key pad device is enabled. */
|
|
static unsigned int key_pad_enabled;
|
|
|
|
/*! Input device structure. */
|
|
static struct input_dev *mxckbd_dev = NULL;
|
|
|
|
/*! KPP clock handle. */
|
|
static struct clk *kpp_clk;
|
|
|
|
/*! This static variable indicates whether a key event is pressed/released. */
|
|
static unsigned short KPress;
|
|
|
|
/*! cur_rcmap and prev_rcmap array is used to detect key press and release. */
|
|
static unsigned short *cur_rcmap; /* max 64 bits (8x8 matrix) */
|
|
static unsigned short *prev_rcmap;
|
|
|
|
/*!
|
|
* Debounce polling period(10ms) in system ticks.
|
|
*/
|
|
static unsigned short KScanRate = (10 * HZ) / 1000;
|
|
|
|
static struct keypad_data *keypad;
|
|
|
|
static int has_leaning_key;
|
|
/*!
|
|
* These arrays are used to store press and release scancodes.
|
|
*/
|
|
static short **press_scancode;
|
|
static short **release_scancode;
|
|
|
|
static const unsigned short *mxckpd_keycodes;
|
|
static unsigned short mxckpd_keycodes_size;
|
|
|
|
#define press_left_code 30
|
|
#define press_right_code 29
|
|
#define press_up_code 28
|
|
#define press_down_code 27
|
|
|
|
#define rel_left_code 158
|
|
#define rel_right_code 157
|
|
#define rel_up_code 156
|
|
#define rel_down_code 155
|
|
/*!
|
|
* These functions are used to configure and the GPIO pins for keypad to
|
|
* activate and deactivate it.
|
|
*/
|
|
extern void gpio_keypad_active(void);
|
|
extern void gpio_keypad_inactive(void);
|
|
|
|
/*!
|
|
* This function is called for generating scancodes for key press and
|
|
* release on keypad for the board.
|
|
*
|
|
* @param row Keypad row pressed on the keypad matrix.
|
|
* @param col Keypad col pressed on the keypad matrix.
|
|
* @param press Indicated key press/release.
|
|
*
|
|
* @return Key press/release Scancode.
|
|
*/
|
|
static signed short mxc_scan_matrix_leaning_key(int row, int col, int press)
|
|
{
|
|
static unsigned first_row;
|
|
static unsigned first_set = 0, flag = 0;
|
|
signed short scancode = -1;
|
|
|
|
if (press) {
|
|
if ((3 == col) && ((3 == row) ||
|
|
(4 == row) || (5 == row) || (6 == row))) {
|
|
if (first_set == 0) {
|
|
first_set = 1;
|
|
first_row = row;
|
|
} else {
|
|
first_set = 0;
|
|
if (((first_row == 6) || (first_row == 3))
|
|
&& ((row == 6) || (row == 3)))
|
|
scancode = press_down_code;
|
|
else if (((first_row == 3) || (first_row == 5))
|
|
&& ((row == 3) || (row == 5)))
|
|
scancode = press_left_code;
|
|
else if (((first_row == 6) || (first_row == 4))
|
|
&& ((row == 6) || (row == 4)))
|
|
scancode = press_right_code;
|
|
else if (((first_row == 4) || (first_row == 5))
|
|
&& ((row == 4) || (row == 5)))
|
|
scancode = press_up_code;
|
|
KPress = 1;
|
|
kpp_dev.iKeyState = KStateUp;
|
|
pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
|
|
row, col, scancode, KPress);
|
|
}
|
|
} else {
|
|
/*
|
|
* check for other keys only
|
|
* if the cursor key presses
|
|
* are not detected may be
|
|
* this needs better logic
|
|
*/
|
|
if ((0 == (cur_rcmap[3] & BITSET(0, 3))) &&
|
|
(0 == (cur_rcmap[4] & BITSET(0, 3))) &&
|
|
(0 == (cur_rcmap[5] & BITSET(0, 3))) &&
|
|
(0 == (cur_rcmap[6] & BITSET(0, 3)))) {
|
|
scancode = ((col * kpp_dev.kpp_rows) + row);
|
|
KPress = 1;
|
|
kpp_dev.iKeyState = KStateUp;
|
|
flag = 1;
|
|
pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
|
|
row, col, scancode, KPress);
|
|
}
|
|
}
|
|
} else {
|
|
if ((flag == 0) && (3 == col)
|
|
&& ((3 == row) || (4 == row) || (5 == row)
|
|
|| (6 == row))) {
|
|
if (first_set == 0) {
|
|
first_set = 1;
|
|
first_row = row;
|
|
} else {
|
|
first_set = 0;
|
|
if (((first_row == 6) || (first_row == 3))
|
|
&& ((row == 6) || (row == 3)))
|
|
scancode = rel_down_code;
|
|
else if (((first_row == 3) || (first_row == 5))
|
|
&& ((row == 3) || (row == 5)))
|
|
scancode = rel_left_code;
|
|
else if (((first_row == 6) || (first_row == 4))
|
|
&& ((row == 6) || (row == 4)))
|
|
scancode = rel_right_code;
|
|
else if (((first_row == 4) || (first_row == 5))
|
|
&& ((row == 4) || (row == 5)))
|
|
scancode = rel_up_code;
|
|
KPress = 0;
|
|
kpp_dev.iKeyState = KStateDown;
|
|
pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
|
|
row, col, scancode, KPress);
|
|
}
|
|
} else {
|
|
/*
|
|
* check for other keys only
|
|
* if the cursor key presses
|
|
* are not detected may be
|
|
* this needs better logic
|
|
*/
|
|
if ((0 == (prev_rcmap[3] & BITSET(0, 3))) &&
|
|
(0 == (prev_rcmap[4] & BITSET(0, 3))) &&
|
|
(0 == (cur_rcmap[5] & BITSET(0, 3))) &&
|
|
(0 == (cur_rcmap[6] & BITSET(0, 3)))) {
|
|
scancode = ((col * kpp_dev.kpp_rows) + row) +
|
|
MXC_KEYRELEASE;
|
|
KPress = 0;
|
|
flag = 0;
|
|
kpp_dev.iKeyState = KStateDown;
|
|
pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
|
|
row, col, scancode, KPress);
|
|
}
|
|
}
|
|
}
|
|
return scancode;
|
|
}
|
|
|
|
/*!
|
|
* This function is called to scan the keypad matrix to find out the key press
|
|
* and key release events. Make scancode and break scancode are generated for
|
|
* key press and key release events.
|
|
*
|
|
* The following scanning sequence are done for
|
|
* keypad row and column scanning,
|
|
* -# Write 1's to KPDR[15:8], setting column data to 1's
|
|
* -# Configure columns as totem pole outputs(for quick discharging of keypad
|
|
* capacitance)
|
|
* -# Configure columns as open-drain
|
|
* -# Write a single column to 0, others to 1.
|
|
* -# Sample row inputs and save data. Multiple key presses can be detected on
|
|
* a single column.
|
|
* -# Repeat steps the above steps for remaining columns.
|
|
* -# Return all columns to 0 in preparation for standby mode.
|
|
* -# Clear KPKD and KPKR status bit(s) by writing to a 1,
|
|
* Set the KPKR synchronizer chain by writing "1" to KRSS register,
|
|
* Clear the KPKD synchronizer chain by writing "1" to KDSC register
|
|
*
|
|
* @result Number of key pressed/released.
|
|
*/
|
|
static int mxc_kpp_scan_matrix(void)
|
|
{
|
|
unsigned short reg_val;
|
|
int col, row;
|
|
short scancode = 0;
|
|
int keycnt = 0; /* How many keys are still pressed */
|
|
|
|
/*
|
|
* wmb() linux kernel function which guarantees orderings in write
|
|
* operations
|
|
*/
|
|
wmb();
|
|
|
|
/* save cur keypad matrix to prev */
|
|
|
|
memcpy(prev_rcmap, cur_rcmap, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
|
|
memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
|
|
|
|
for (col = 0; col < kpp_dev.kpp_cols; col++) { /* Col */
|
|
/* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
|
|
reg_val = __raw_readw(kpp_dev.base + KPDR);
|
|
reg_val |= 0xff00;
|
|
__raw_writew(reg_val, kpp_dev.base + KPDR);
|
|
|
|
/*
|
|
* 3. Configure columns as totem pole outputs(for quick
|
|
* discharging of keypad capacitance)
|
|
*/
|
|
reg_val = __raw_readw(kpp_dev.base + KPCR);
|
|
reg_val &= 0x00ff;
|
|
__raw_writew(reg_val, kpp_dev.base + KPCR);
|
|
|
|
udelay(2);
|
|
|
|
/*
|
|
* 4. Configure columns as open-drain
|
|
*/
|
|
reg_val = __raw_readw(kpp_dev.base + KPCR);
|
|
reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
|
|
__raw_writew(reg_val, kpp_dev.base + KPCR);
|
|
|
|
/*
|
|
* 5. Write a single column to 0, others to 1.
|
|
* 6. Sample row inputs and save data. Multiple key presses
|
|
* can be detected on a single column.
|
|
* 7. Repeat steps 2 - 6 for remaining columns.
|
|
*/
|
|
|
|
/* Col bit starts at 8th bit in KPDR */
|
|
reg_val = __raw_readw(kpp_dev.base + KPDR);
|
|
reg_val &= ~(1 << (8 + col));
|
|
__raw_writew(reg_val, kpp_dev.base + KPDR);
|
|
|
|
/* Delay added to avoid propagating the 0 from column to row
|
|
* when scanning. */
|
|
|
|
udelay(5);
|
|
|
|
/* Read row input */
|
|
reg_val = __raw_readw(kpp_dev.base + KPDR);
|
|
for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */
|
|
if (TEST_BIT(reg_val, row) == 0) {
|
|
cur_rcmap[row] = BITSET(cur_rcmap[row], col);
|
|
keycnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 8. Return all columns to 0 in preparation for standby mode.
|
|
* 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
|
|
* set the KPKR synchronizer chain by writing "1" to KRSS register,
|
|
* clear the KPKD synchronizer chain by writing "1" to KDSC register
|
|
*/
|
|
reg_val = 0x00;
|
|
__raw_writew(reg_val, kpp_dev.base + KPDR);
|
|
reg_val = __raw_readw(kpp_dev.base + KPDR);
|
|
reg_val = __raw_readw(kpp_dev.base + KPSR);
|
|
reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS |
|
|
KBD_STAT_KDSC;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
|
|
/* Check key press status change */
|
|
|
|
/*
|
|
* prev_rcmap array will contain the previous status of the keypad
|
|
* matrix. cur_rcmap array will contains the present status of the
|
|
* keypad matrix. If a bit is set in the array, that (row, col) bit is
|
|
* pressed, else it is not pressed.
|
|
*
|
|
* XORing these two variables will give us the change in bit for
|
|
* particular row and column. If a bit is set in XOR output, then that
|
|
* (row, col) has a change of status from the previous state. From
|
|
* the diff variable the key press and key release of row and column
|
|
* are found out.
|
|
*
|
|
* If the key press is determined then scancode for key pressed
|
|
* can be generated using the following statement:
|
|
* scancode = ((row * 8) + col);
|
|
*
|
|
* If the key release is determined then scancode for key release
|
|
* can be generated using the following statement:
|
|
* scancode = ((row * 8) + col) + MXC_KEYRELEASE;
|
|
*/
|
|
for (row = 0; row < kpp_dev.kpp_rows; row++) {
|
|
unsigned char diff;
|
|
|
|
/*
|
|
* Calculate the change in the keypad row status
|
|
*/
|
|
diff = prev_rcmap[row] ^ cur_rcmap[row];
|
|
|
|
for (col = 0; col < kpp_dev.kpp_cols; col++) {
|
|
if ((diff >> col) & 0x1) {
|
|
/* There is a status change on col */
|
|
if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
|
|
/*
|
|
* Previous state is 0, so now
|
|
* a key is pressed
|
|
*/
|
|
if (has_leaning_key) {
|
|
scancode =
|
|
mxc_scan_matrix_leaning_key
|
|
(row, col, 1);
|
|
} else {
|
|
scancode =
|
|
((row * kpp_dev.kpp_cols) +
|
|
col);
|
|
KPress = 1;
|
|
kpp_dev.iKeyState = KStateUp;
|
|
}
|
|
pr_debug("Press (%d, %d) scan=%d "
|
|
"Kpress=%d\n",
|
|
row, col, scancode, KPress);
|
|
press_scancode[row][col] =
|
|
(short)scancode;
|
|
} else {
|
|
/*
|
|
* Previous state is not 0, so
|
|
* now a key is released
|
|
*/
|
|
if (has_leaning_key) {
|
|
scancode =
|
|
mxc_scan_matrix_leaning_key
|
|
(row, col, 0);
|
|
} else {
|
|
scancode =
|
|
(row * kpp_dev.kpp_cols) +
|
|
col + MXC_KEYRELEASE;
|
|
KPress = 0;
|
|
kpp_dev.iKeyState = KStateDown;
|
|
}
|
|
|
|
pr_debug
|
|
("Release (%d, %d) scan=%d Kpress=%d\n",
|
|
row, col, scancode, KPress);
|
|
release_scancode[row][col] =
|
|
(short)scancode;
|
|
keycnt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This switch case statement is the
|
|
* implementation of state machine of debounce
|
|
* logic for key press/release.
|
|
* The explaination of state machine is as
|
|
* follows:
|
|
*
|
|
* KStateUp State:
|
|
* This is in intial state of the state machine
|
|
* this state it checks for any key presses.
|
|
* The key press can be checked using the
|
|
* variable KPress. If KPress is set, then key
|
|
* press is identified and switches the to
|
|
* KStateFirstDown state for key press to
|
|
* debounce.
|
|
*
|
|
* KStateFirstDown:
|
|
* After debounce delay(10ms), if the KPress is
|
|
* still set then pass scancode generated to
|
|
* input device and change the state to
|
|
* KStateDown, else key press debounce is not
|
|
* satisfied so change the state to KStateUp.
|
|
*
|
|
* KStateDown:
|
|
* In this state it checks for any key release.
|
|
* If KPress variable is cleared, then key
|
|
* release is indicated and so, switch the
|
|
* state to KStateFirstUp else to state
|
|
* KStateDown.
|
|
*
|
|
* KStateFirstUp:
|
|
* After debounce delay(10ms), if the KPress is
|
|
* still reset then pass the key release
|
|
* scancode to input device and change
|
|
* the state to KStateUp else key release is
|
|
* not satisfied so change the state to
|
|
* KStateDown.
|
|
*/
|
|
switch (kpp_dev.iKeyState) {
|
|
case KStateUp:
|
|
if (KPress) {
|
|
/* First Down (must debounce). */
|
|
kpp_dev.iKeyState = KStateFirstDown;
|
|
} else {
|
|
/* Still UP.(NO Changes) */
|
|
kpp_dev.iKeyState = KStateUp;
|
|
}
|
|
break;
|
|
|
|
case KStateFirstDown:
|
|
if (KPress) {
|
|
for (row = 0; row < kpp_dev.kpp_rows; row++) {
|
|
for (col = 0; col < kpp_dev.kpp_cols; col++) {
|
|
if ((press_scancode[row][col] != -1)) {
|
|
/* Still Down, so add scancode */
|
|
scancode =
|
|
press_scancode[row][col];
|
|
input_event(mxckbd_dev, EV_KEY,
|
|
mxckpd_keycodes
|
|
[scancode], 1);
|
|
if (mxckpd_keycodes[scancode] ==
|
|
KEY_LEFTSHIFT) {
|
|
input_event(mxckbd_dev,
|
|
EV_KEY,
|
|
KEY_3, 1);
|
|
}
|
|
kpp_dev.iKeyState = KStateDown;
|
|
press_scancode[row][col] = -1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* Just a bounce */
|
|
kpp_dev.iKeyState = KStateUp;
|
|
}
|
|
break;
|
|
|
|
case KStateDown:
|
|
if (KPress) {
|
|
/* Still down (no change) */
|
|
kpp_dev.iKeyState = KStateDown;
|
|
} else {
|
|
/* First Up. Must debounce */
|
|
kpp_dev.iKeyState = KStateFirstUp;
|
|
}
|
|
break;
|
|
|
|
case KStateFirstUp:
|
|
if (KPress) {
|
|
/* Just a bounce */
|
|
kpp_dev.iKeyState = KStateDown;
|
|
} else {
|
|
for (row = 0; row < kpp_dev.kpp_rows; row++) {
|
|
for (col = 0; col < kpp_dev.kpp_cols; col++) {
|
|
if ((release_scancode[row][col] != -1)) {
|
|
scancode =
|
|
release_scancode[row][col];
|
|
scancode =
|
|
scancode - MXC_KEYRELEASE;
|
|
input_event(mxckbd_dev, EV_KEY,
|
|
mxckpd_keycodes
|
|
[scancode], 0);
|
|
if (mxckpd_keycodes[scancode] ==
|
|
KEY_LEFTSHIFT) {
|
|
input_event(mxckbd_dev,
|
|
EV_KEY,
|
|
KEY_3, 0);
|
|
}
|
|
kpp_dev.iKeyState = KStateUp;
|
|
release_scancode[row][col] = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -EBADRQC;
|
|
break;
|
|
}
|
|
|
|
return keycnt;
|
|
}
|
|
|
|
/*!
|
|
* This function is called to start the timer for scanning the keypad if there
|
|
* is any key press. Currently this interval is set to 10 ms. When there are
|
|
* no keys pressed on the keypad we return back, waiting for a keypad key
|
|
* press interrupt.
|
|
*
|
|
* @param data Opaque data passed back by kernel. Not used.
|
|
*/
|
|
static void mxc_kpp_handle_timer(unsigned long data)
|
|
{
|
|
unsigned short reg_val;
|
|
int i;
|
|
|
|
if (key_pad_enabled == 0) {
|
|
return;
|
|
}
|
|
if (mxc_kpp_scan_matrix() == 0) {
|
|
/*
|
|
* Stop scanning and wait for interrupt.
|
|
* Enable press interrupt and disable release interrupt.
|
|
*/
|
|
__raw_writew(0x00FF, kpp_dev.base + KPDR);
|
|
reg_val = __raw_readw(kpp_dev.base + KPSR);
|
|
reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD);
|
|
reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
reg_val |= KBD_STAT_KDIE;
|
|
reg_val &= ~KBD_STAT_KRIE;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
|
|
/*
|
|
* No more keys pressed... make sure unwanted key codes are
|
|
* not given upstairs
|
|
*/
|
|
for (i = 0; i < kpp_dev.kpp_rows; i++) {
|
|
memset(press_scancode[i], -1,
|
|
sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
|
|
memset(release_scancode[i], -1,
|
|
sizeof(release_scancode[0][0]) *
|
|
kpp_dev.kpp_cols);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* There are still some keys pressed, continue to scan.
|
|
* We shall scan again in 10 ms. This has to be tuned according
|
|
* to the requirement.
|
|
*/
|
|
kpp_dev.poll_timer.expires = jiffies + KScanRate;
|
|
kpp_dev.poll_timer.function = mxc_kpp_handle_timer;
|
|
add_timer(&kpp_dev.poll_timer);
|
|
}
|
|
|
|
/*!
|
|
* This function is the keypad Interrupt handler.
|
|
* This function checks for keypad status register (KPSR) for key press
|
|
* and interrupt. If key press interrupt has occurred, then the key
|
|
* press interrupt in the KPSR are disabled.
|
|
* It then calls mxc_kpp_scan_matrix to check for any key pressed/released.
|
|
* If any key is found to be pressed, then a timer is set to call
|
|
* mxc_kpp_scan_matrix function for every 10 ms.
|
|
*
|
|
* @param irq The Interrupt number
|
|
* @param dev_id Driver private data
|
|
*
|
|
* @result The function returns \b IRQ_RETVAL(1) if interrupt was handled,
|
|
* returns \b IRQ_RETVAL(0) if the interrupt was not handled.
|
|
* \b IRQ_RETVAL is defined in include/linux/interrupt.h.
|
|
*/
|
|
static irqreturn_t mxc_kpp_interrupt(int irq, void *dev_id)
|
|
{
|
|
unsigned short reg_val;
|
|
|
|
/* Delete the polling timer */
|
|
del_timer(&kpp_dev.poll_timer);
|
|
reg_val = __raw_readw(kpp_dev.base + KPSR);
|
|
|
|
/* Check if it is key press interrupt */
|
|
if (reg_val & KBD_STAT_KPKD) {
|
|
/*
|
|
* Disable key press(KDIE status bit) interrupt
|
|
*/
|
|
reg_val &= ~KBD_STAT_KDIE;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
} else {
|
|
/* spurious interrupt */
|
|
return IRQ_RETVAL(0);
|
|
}
|
|
/*
|
|
* Check if any keys are pressed, if so start polling.
|
|
*/
|
|
mxc_kpp_handle_timer(0);
|
|
|
|
return IRQ_RETVAL(1);
|
|
}
|
|
|
|
/*!
|
|
* This function is called when the keypad driver is opened.
|
|
* Since keypad initialization is done in __init, nothing is done in open.
|
|
*
|
|
* @param dev Pointer to device inode
|
|
*
|
|
* @result The function always return 0
|
|
*/
|
|
static int mxc_kpp_open(struct input_dev *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* This function is called close the keypad device.
|
|
* Nothing is done in this function, since every thing is taken care in
|
|
* __exit function.
|
|
*
|
|
* @param dev Pointer to device inode
|
|
*
|
|
*/
|
|
static void mxc_kpp_close(struct input_dev *dev)
|
|
{
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
/*!
|
|
* This function puts the Keypad controller in low-power mode/state.
|
|
* If Keypad is enabled as a wake source(i.e. it can resume the system
|
|
* from suspend mode), the Keypad controller doesn't enter low-power state.
|
|
*
|
|
* @param pdev the device structure used to give information on Keypad
|
|
* to suspend
|
|
* @param state the power state the device is entering
|
|
*
|
|
* @return return -1 when the keypad is pressed. Otherwise, return 0
|
|
*/
|
|
static int mxc_kpp_suspend(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
/* When the keypad is still pressed, clean up registers and timers */
|
|
if (timer_pending(&kpp_dev.poll_timer))
|
|
return -1;
|
|
|
|
if (device_may_wakeup(&pdev->dev)) {
|
|
enable_irq_wake(keypad->irq);
|
|
} else {
|
|
disable_irq(keypad->irq);
|
|
key_pad_enabled = 0;
|
|
clk_disable(kpp_clk);
|
|
gpio_keypad_inactive();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* This function brings the Keypad controller back from low-power state.
|
|
* If Keypad is enabled as a wake source(i.e. it can resume the system
|
|
* from suspend mode), the Keypad controller doesn't enter low-power state.
|
|
*
|
|
* @param pdev the device structure used to give information on Keypad
|
|
* to resume
|
|
*
|
|
* @return The function always returns 0.
|
|
*/
|
|
static int mxc_kpp_resume(struct platform_device *pdev)
|
|
{
|
|
if (device_may_wakeup(&pdev->dev)) {
|
|
disable_irq_wake(keypad->irq);
|
|
} else {
|
|
gpio_keypad_active();
|
|
clk_enable(kpp_clk);
|
|
key_pad_enabled = 1;
|
|
enable_irq(keypad->irq);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
#define mxc_kpp_suspend NULL
|
|
#define mxc_kpp_resume NULL
|
|
#endif /* CONFIG_PM */
|
|
|
|
/*!
|
|
* This function is called to free the allocated memory for local arrays
|
|
*/
|
|
static void mxc_kpp_free_allocated(void)
|
|
{
|
|
|
|
int i;
|
|
|
|
if (press_scancode) {
|
|
for (i = 0; i < kpp_dev.kpp_rows; i++) {
|
|
if (press_scancode[i])
|
|
kfree(press_scancode[i]);
|
|
}
|
|
kfree(press_scancode);
|
|
}
|
|
|
|
if (release_scancode) {
|
|
for (i = 0; i < kpp_dev.kpp_rows; i++) {
|
|
if (release_scancode[i])
|
|
kfree(release_scancode[i]);
|
|
}
|
|
kfree(release_scancode);
|
|
}
|
|
|
|
if (cur_rcmap)
|
|
kfree(cur_rcmap);
|
|
|
|
if (prev_rcmap)
|
|
kfree(prev_rcmap);
|
|
|
|
if (mxckbd_dev)
|
|
input_free_device(mxckbd_dev);
|
|
}
|
|
|
|
/*!
|
|
* This function is called during the driver binding process.
|
|
*
|
|
* @param pdev the device structure used to store device specific
|
|
* information that is used by the suspend, resume and remove
|
|
* functions.
|
|
*
|
|
* @return The function returns 0 on successful registration. Otherwise returns
|
|
* specific error code.
|
|
*/
|
|
static int mxc_kpp_probe(struct platform_device *pdev)
|
|
{
|
|
int i, irq;
|
|
int retval;
|
|
unsigned int reg_val;
|
|
struct resource *res;
|
|
|
|
keypad = (struct keypad_data *)pdev->dev.platform_data;
|
|
|
|
kpp_dev.kpp_cols = keypad->colmax;
|
|
kpp_dev.kpp_rows = keypad->rowmax;
|
|
key_pad_enabled = 0;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res)
|
|
return -ENODEV;
|
|
|
|
kpp_dev.base = ioremap(res->start, res->end - res->start + 1);
|
|
if (!kpp_dev.base)
|
|
return -ENOMEM;
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
keypad->irq = irq;
|
|
|
|
/* Enable keypad clock */
|
|
kpp_clk = clk_get(&pdev->dev, "kpp_clk");
|
|
clk_enable(kpp_clk);
|
|
|
|
/* IOMUX configuration for keypad */
|
|
gpio_keypad_active();
|
|
|
|
/* Configure keypad */
|
|
|
|
/* Enable number of rows in keypad (KPCR[7:0])
|
|
* Configure keypad columns as open-drain (KPCR[15:8])
|
|
*
|
|
* Configure the rows/cols in KPP
|
|
* LSB nibble in KPP is for 8 rows
|
|
* MSB nibble in KPP is for 8 cols
|
|
*/
|
|
reg_val = __raw_readw(kpp_dev.base + KPCR);
|
|
reg_val |= (1 << keypad->rowmax) - 1; /* LSB */
|
|
reg_val |= ((1 << keypad->colmax) - 1) << 8; /* MSB */
|
|
__raw_writew(reg_val, kpp_dev.base + KPCR);
|
|
|
|
/* Write 0's to KPDR[15:8] */
|
|
reg_val = __raw_readw(kpp_dev.base + KPDR);
|
|
reg_val &= 0x00ff;
|
|
__raw_writew(reg_val, kpp_dev.base + KPDR);
|
|
|
|
/* Configure columns as output, rows as input (KDDR[15:0]) */
|
|
reg_val = __raw_readw(kpp_dev.base + KDDR);
|
|
reg_val |= 0xff00;
|
|
reg_val &= 0xff00;
|
|
__raw_writew(reg_val, kpp_dev.base + KDDR);
|
|
|
|
reg_val = __raw_readw(kpp_dev.base + KPSR);
|
|
reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD);
|
|
reg_val |= KBD_STAT_KPKD;
|
|
reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
reg_val |= KBD_STAT_KDIE;
|
|
reg_val &= ~KBD_STAT_KRIE;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
|
|
has_leaning_key = keypad->learning;
|
|
mxckpd_keycodes = keypad->matrix;
|
|
mxckpd_keycodes_size = keypad->rowmax * keypad->colmax;
|
|
|
|
if ((keypad->matrix == (void *)0)
|
|
|| (mxckpd_keycodes_size == 0)) {
|
|
retval = -ENODEV;
|
|
goto err1;
|
|
}
|
|
|
|
mxckbd_dev = input_allocate_device();
|
|
if (!mxckbd_dev) {
|
|
printk(KERN_ERR
|
|
"mxckbd_dev: not enough memory for input device\n");
|
|
retval = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
mxckbd_dev->keycode = (void *)mxckpd_keycodes;
|
|
mxckbd_dev->keycodesize = sizeof(mxckpd_keycodes[0]);
|
|
mxckbd_dev->keycodemax = mxckpd_keycodes_size;
|
|
mxckbd_dev->name = "mxckpd";
|
|
mxckbd_dev->id.bustype = BUS_HOST;
|
|
mxckbd_dev->open = mxc_kpp_open;
|
|
mxckbd_dev->close = mxc_kpp_close;
|
|
|
|
retval = input_register_device(mxckbd_dev);
|
|
if (retval < 0) {
|
|
printk(KERN_ERR
|
|
"mxckbd_dev: failed to register input device\n");
|
|
goto err2;
|
|
}
|
|
|
|
/* allocate required memory */
|
|
press_scancode = kmalloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]),
|
|
GFP_KERNEL);
|
|
release_scancode =
|
|
kmalloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]), GFP_KERNEL);
|
|
|
|
if (!press_scancode || !release_scancode) {
|
|
retval = -ENOMEM;
|
|
goto err3;
|
|
}
|
|
|
|
for (i = 0; i < kpp_dev.kpp_rows; i++) {
|
|
press_scancode[i] = kmalloc(kpp_dev.kpp_cols
|
|
* sizeof(press_scancode[0][0]),
|
|
GFP_KERNEL);
|
|
release_scancode[i] =
|
|
kmalloc(kpp_dev.kpp_cols * sizeof(release_scancode[0][0]),
|
|
GFP_KERNEL);
|
|
|
|
if (!press_scancode[i] || !release_scancode[i]) {
|
|
retval = -ENOMEM;
|
|
goto err3;
|
|
}
|
|
}
|
|
|
|
cur_rcmap =
|
|
kmalloc(kpp_dev.kpp_rows * sizeof(cur_rcmap[0]), GFP_KERNEL);
|
|
prev_rcmap =
|
|
kmalloc(kpp_dev.kpp_rows * sizeof(prev_rcmap[0]), GFP_KERNEL);
|
|
|
|
if (!cur_rcmap || !prev_rcmap) {
|
|
retval = -ENOMEM;
|
|
goto err3;
|
|
}
|
|
|
|
__set_bit(EV_KEY, mxckbd_dev->evbit);
|
|
|
|
for (i = 0; i < mxckpd_keycodes_size; i++)
|
|
__set_bit(mxckpd_keycodes[i], mxckbd_dev->keybit);
|
|
|
|
for (i = 0; i < kpp_dev.kpp_rows; i++) {
|
|
memset(press_scancode[i], -1,
|
|
sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
|
|
memset(release_scancode[i], -1,
|
|
sizeof(release_scancode[0][0]) * kpp_dev.kpp_cols);
|
|
}
|
|
memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
|
|
memset(prev_rcmap, 0, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
|
|
|
|
key_pad_enabled = 1;
|
|
/* Initialize the polling timer */
|
|
init_timer(&kpp_dev.poll_timer);
|
|
|
|
/*
|
|
* Request for IRQ number for keypad port. The Interrupt handler
|
|
* function (mxc_kpp_interrupt) is called when ever interrupt occurs on
|
|
* keypad port.
|
|
*/
|
|
retval = request_irq(irq, mxc_kpp_interrupt, 0, MOD_NAME, MOD_NAME);
|
|
if (retval) {
|
|
pr_debug("KPP: request_irq(%d) returned error %d\n",
|
|
MXC_INT_KPP, retval);
|
|
goto err3;
|
|
}
|
|
|
|
/* By default, devices should wakeup if they can */
|
|
/* So keypad is set as "should wakeup" as it can */
|
|
device_init_wakeup(&pdev->dev, 1);
|
|
|
|
return 0;
|
|
|
|
err3:
|
|
mxc_kpp_free_allocated();
|
|
err2:
|
|
input_free_device(mxckbd_dev);
|
|
err1:
|
|
free_irq(irq, MOD_NAME);
|
|
clk_disable(kpp_clk);
|
|
clk_put(kpp_clk);
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* Dissociates the driver from the kpp device.
|
|
*
|
|
* @param pdev the device structure used to give information on which SDHC
|
|
* to remove
|
|
*
|
|
* @return The function always returns 0.
|
|
*/
|
|
static int mxc_kpp_remove(struct platform_device *pdev)
|
|
{
|
|
unsigned short reg_val;
|
|
|
|
/*
|
|
* Clear the KPKD status flag (write 1 to it) and synchronizer chain.
|
|
* Set KDIE control bit, clear KRIE control bit (avoid false release
|
|
* events. Disable the keypad GPIO pins.
|
|
*/
|
|
__raw_writew(0x00, kpp_dev.base + KPCR);
|
|
__raw_writew(0x00, kpp_dev.base + KPDR);
|
|
__raw_writew(0x00, kpp_dev.base + KDDR);
|
|
|
|
reg_val = __raw_readw(kpp_dev.base + KPSR);
|
|
reg_val |= KBD_STAT_KPKD;
|
|
reg_val &= ~KBD_STAT_KRSS;
|
|
reg_val |= KBD_STAT_KDIE;
|
|
reg_val &= ~KBD_STAT_KRIE;
|
|
__raw_writew(reg_val, kpp_dev.base + KPSR);
|
|
|
|
gpio_keypad_inactive();
|
|
clk_disable(kpp_clk);
|
|
clk_put(kpp_clk);
|
|
|
|
KPress = 0;
|
|
|
|
del_timer(&kpp_dev.poll_timer);
|
|
|
|
free_irq(keypad->irq, MOD_NAME);
|
|
input_unregister_device(mxckbd_dev);
|
|
|
|
mxc_kpp_free_allocated();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* This structure contains pointers to the power management callback functions.
|
|
*/
|
|
static struct platform_driver mxc_kpd_driver = {
|
|
.driver = {
|
|
.name = "mxc_keypad",
|
|
.bus = &platform_bus_type,
|
|
},
|
|
.suspend = mxc_kpp_suspend,
|
|
.resume = mxc_kpp_resume,
|
|
.probe = mxc_kpp_probe,
|
|
.remove = mxc_kpp_remove
|
|
};
|
|
|
|
/*!
|
|
* This function is called for module initialization.
|
|
* It registers keypad char driver and requests for KPP irq number. This
|
|
* function does the initialization of the keypad device.
|
|
*
|
|
* The following steps are used for keypad configuration,\n
|
|
* -# Enable number of rows in the keypad control register (KPCR[7:0}).\n
|
|
* -# Write 0's to KPDR[15:8]\n
|
|
* -# Configure keypad columns as open-drain (KPCR[15:8])\n
|
|
* -# Configure columns as output, rows as input (KDDR[15:0])\n
|
|
* -# Clear the KPKD status flag (write 1 to it) and synchronizer chain\n
|
|
* -# Set KDIE control bit, clear KRIE control bit\n
|
|
* In this function the keypad queue initialization is done.
|
|
* The keypad IOMUX configuration are done here.*
|
|
|
|
*
|
|
* @return 0 on success and a non-zero value on failure.
|
|
*/
|
|
static int __init mxc_kpp_init(void)
|
|
{
|
|
printk(KERN_INFO "MXC keypad loaded\n");
|
|
platform_driver_register(&mxc_kpd_driver);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* This function is called whenever the module is removed from the kernel. It
|
|
* unregisters the keypad driver from kernel and frees the irq number.
|
|
* This function puts the keypad to standby mode. The keypad interrupts are
|
|
* disabled. It calls gpio_keypad_inactive function to switch gpio
|
|
* configuration into default state.
|
|
*
|
|
*/
|
|
static void __exit mxc_kpp_cleanup(void)
|
|
{
|
|
platform_driver_unregister(&mxc_kpd_driver);
|
|
}
|
|
|
|
module_init(mxc_kpp_init);
|
|
module_exit(mxc_kpp_cleanup);
|
|
|
|
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
|
MODULE_DESCRIPTION("MXC Keypad Controller Driver");
|
|
MODULE_LICENSE("GPL");
|