Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,644 @@
#
# Input core configuration
#
menuconfig INPUT_KEYBOARD
bool "Keyboards" if EXPERT || !X86
default y
help
Say Y here, and a list of supported keyboards will be displayed.
This option doesn't affect the kernel.
If unsure, say Y.
if INPUT_KEYBOARD
config KEYBOARD_ADP5520
tristate "Keypad Support for ADP5520 PMIC"
depends on PMIC_ADP5520
help
This option enables support for the keypad scan matrix
on Analog Devices ADP5520 PMICs.
To compile this driver as a module, choose M here: the module will
be called adp5520-keys.
config KEYBOARD_ADP5588
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
depends on I2C
help
Say Y here if you want to use a ADP5588/87 attached to your
system I2C bus.
To compile this driver as a module, choose M here: the
module will be called adp5588-keys.
config KEYBOARD_ADP5589
tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander"
depends on I2C
help
Say Y here if you want to use a ADP5585/ADP5589 attached to your
system I2C bus.
To compile this driver as a module, choose M here: the
module will be called adp5589-keys.
config KEYBOARD_AMIGA
tristate "Amiga keyboard"
depends on AMIGA
help
Say Y here if you are running Linux on any AMIGA and have a keyboard
attached.
To compile this driver as a module, choose M here: the
module will be called amikbd.
config ATARI_KBD_CORE
bool
config KEYBOARD_ATARI
tristate "Atari keyboard"
depends on ATARI
select ATARI_KBD_CORE
help
Say Y here if you are running Linux on any Atari and have a keyboard
attached.
To compile this driver as a module, choose M here: the
module will be called atakbd.
config KEYBOARD_ATKBD
tristate "AT keyboard" if EXPERT || !X86
default y
select SERIO
select SERIO_LIBPS2
select SERIO_I8042 if X86
select SERIO_GSCPS2 if GSC
help
Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
you'll need this, unless you have a different type keyboard (USB, ADB
or other). This also works for AT and PS/2 keyboards connected over a
PS/2 to serial converter.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called atkbd.
config KEYBOARD_ATKBD_HP_KEYCODES
bool "Use HP keyboard scancodes"
depends on PARISC && KEYBOARD_ATKBD
default y
help
Say Y here if you have a PA-RISC machine and want to use an AT or
PS/2 keyboard, and your keyboard uses keycodes that are specific to
PA-RISC keyboards.
Say N if you use a standard keyboard.
config KEYBOARD_ATKBD_RDI_KEYCODES
bool "Use PrecisionBook keyboard scancodes"
depends on KEYBOARD_ATKBD_HP_KEYCODES
default n
help
If you have an RDI PrecisionBook, say Y here if you want to use its
built-in keyboard (as opposed to an external keyboard).
The PrecisionBook has five keys that conflict with those used by most
AT and PS/2 keyboards. These are as follows:
PrecisionBook Standard AT or PS/2
F1 F12
Left Ctrl Left Alt
Caps Lock Left Ctrl
Right Ctrl Caps Lock
Left 102nd key (the key to the right of Left Shift)
If you say N here, and use the PrecisionBook keyboard, then each key
in the left-hand column will be interpreted as the corresponding key
in the right-hand column.
If you say Y here, and use an external keyboard, then each key in the
right-hand column will be interpreted as the key shown in the
left-hand column.
config KEYBOARD_QT1070
tristate "Atmel AT42QT1070 Touch Sensor Chip"
depends on I2C
help
Say Y here if you want to use Atmel AT42QT1070 QTouch
Sensor chip as input device.
To compile this driver as a module, choose M here:
the module will be called qt1070
config KEYBOARD_QT2160
tristate "Atmel AT42QT2160 Touch Sensor Chip"
depends on I2C
help
If you say yes here you get support for Atmel AT42QT2160 Touch
Sensor chip as a keyboard input.
This driver can also be built as a module. If so, the module
will be called qt2160.
config KEYBOARD_BFIN
tristate "Blackfin BF54x keypad support"
depends on (BF54x && !BF544)
help
Say Y here if you want to use the BF54x keypad.
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
config KEYBOARD_LKKBD
tristate "DECstation/VAXstation LK201/LK401 keyboard"
select SERIO
help
Say Y here if you want to use a LK201 or LK401 style serial
keyboard. This keyboard is also useable on PCs if you attach
it with the inputattach program. The connector pinout is
described within lkkbd.c.
To compile this driver as a module, choose M here: the
module will be called lkkbd.
config KEYBOARD_EP93XX
tristate "EP93xx Matrix Keypad support"
depends on ARCH_EP93XX
select INPUT_MATRIXKMAP
help
Say Y here to enable the matrix keypad on the Cirrus EP93XX.
To compile this driver as a module, choose M here: the
module will be called ep93xx_keypad.
config KEYBOARD_GPIO
tristate "GPIO Buttons"
depends on GPIOLIB
help
This driver implements support for buttons connected
to GPIO pins of various CPUs (and some other chips).
Say Y here if your device has buttons connected
directly to such GPIO pins. Your board-specific
setup logic must also provide a platform device,
with configuration data saying which GPIOs are used.
To compile this driver as a module, choose M here: the
module will be called gpio_keys.
config KEYBOARD_GPIO_POLLED
tristate "Polled GPIO buttons"
depends on GPIOLIB
select INPUT_POLLDEV
help
This driver implements support for buttons connected
to GPIO pins that are not capable of generating interrupts.
Say Y here if your device has buttons connected
directly to such GPIO pins. Your board-specific
setup logic must also provide a platform device,
with configuration data saying which GPIOs are used.
To compile this driver as a module, choose M here: the
module will be called gpio_keys_polled.
config KEYBOARD_TCA6416
tristate "TCA6416/TCA6408A Keypad Support"
depends on I2C
help
This driver implements basic keypad functionality
for keys connected through TCA6416/TCA6408A IO expanders.
Say Y here if your device has keys connected to
TCA6416/TCA6408A IO expander. Your board-specific setup logic
must also provide pin-mask details(of which TCA6416 pins
are used for keypad).
If enabled the entire TCA6416 device will be managed through
this driver.
To compile this driver as a module, choose M here: the
module will be called tca6416_keypad.
config KEYBOARD_TCA8418
tristate "TCA8418 Keypad Support"
depends on I2C && GENERIC_HARDIRQS
select INPUT_MATRIXKMAP
help
This driver implements basic keypad functionality
for keys connected through TCA8418 keypad decoder.
Say Y here if your device has keys connected to
TCA8418 keypad decoder.
If enabled the complete TCA8418 device will be managed through
this driver.
To compile this driver as a module, choose M here: the
module will be called tca8418_keypad.
config KEYBOARD_MATRIX
tristate "GPIO driven matrix keypad support"
depends on GPIOLIB
select INPUT_MATRIXKMAP
help
Enable support for GPIO driven matrix keypad.
To compile this driver as a module, choose M here: the
module will be called matrix_keypad.
config KEYBOARD_HIL_OLD
tristate "HP HIL keyboard support (simple driver)"
depends on GSC || HP300
default y
help
The "Human Interface Loop" is a older, 8-channel USB-like
controller used in several Hewlett Packard models. This driver
was adapted from the one written for m68k/hp300, and implements
support for a keyboard attached to the HIL port, but not for
any other types of HIL input devices like mice or tablets.
However, it has been thoroughly tested and is stable.
If you want full HIL support including support for multiple
keyboards, mice, and tablets, you have to enable the
"HP System Device Controller i8042 Support" in the input/serio
submenu.
config KEYBOARD_HIL
tristate "HP HIL keyboard/pointer support"
depends on GSC || HP300
default y
select HP_SDC
select HIL_MLC
select SERIO
help
The "Human Interface Loop" is a older, 8-channel USB-like
controller used in several Hewlett Packard models.
This driver implements support for HIL-keyboards and pointing
devices (mice, tablets, touchscreens) attached
to your machine, so normally you should say Y here.
config KEYBOARD_HP6XX
tristate "HP Jornada 6xx keyboard"
depends on SH_HP6XX
select INPUT_POLLDEV
help
Say Y here if you have a HP Jornada 620/660/680/690 and want to
support the built-in keyboard.
To compile this driver as a module, choose M here: the
module will be called jornada680_kbd.
config KEYBOARD_HP7XX
tristate "HP Jornada 7xx keyboard"
depends on SA1100_JORNADA720_SSP && SA1100_SSP
help
Say Y here if you have a HP Jornada 710/720/728 and want to
support the built-in keyboard.
To compile this driver as a module, choose M here: the
module will be called jornada720_kbd.
config KEYBOARD_LM8323
tristate "LM8323 keypad chip"
depends on I2C && GENERIC_HARDIRQS
depends on LEDS_CLASS
help
If you say yes here you get support for the National Semiconductor
LM8323 keypad controller.
To compile this driver as a module, choose M here: the
module will be called lm8323.
config KEYBOARD_LM8333
tristate "LM8333 keypad chip"
depends on I2C
select INPUT_MATRIXKMAP
help
If you say yes here you get support for the National Semiconductor
LM8333 keypad controller.
To compile this driver as a module, choose M here: the
module will be called lm8333.
config KEYBOARD_LOCOMO
tristate "LoCoMo Keyboard Support"
depends on SHARP_LOCOMO
help
Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
To compile this driver as a module, choose M here: the
module will be called locomokbd.
config KEYBOARD_LPC32XX
tristate "LPC32XX matrix key scanner support"
depends on ARCH_LPC32XX && OF
select INPUT_MATRIXKMAP
help
Say Y here if you want to use NXP LPC32XX SoC key scanner interface,
connected to a key matrix.
To compile this driver as a module, choose M here: the
module will be called lpc32xx-keys.
config KEYBOARD_MAPLE
tristate "Maple bus keyboard"
depends on SH_DREAMCAST && MAPLE
help
Say Y here if you have a Dreamcast console running Linux and have
a keyboard attached to its Maple bus.
To compile this driver as a module, choose M here: the
module will be called maple_keyb.
config KEYBOARD_MAX7359
tristate "Maxim MAX7359 Key Switch Controller"
depends on I2C
help
If you say yes here you get support for the Maxim MAX7359 Key
Switch Controller chip. This providers microprocessors with
management of up to 64 key switches
To compile this driver as a module, choose M here: the
module will be called max7359_keypad.
config KEYBOARD_MCS
tristate "MELFAS MCS Touchkey"
depends on I2C
help
Say Y here if you have the MELFAS MCS5000/5080 touchkey controller
chip in your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called mcs_touchkey.
config KEYBOARD_MPR121
tristate "Freescale MPR121 Touchkey"
depends on I2C
help
Say Y here if you have Freescale MPR121 touchkey controller
chip in your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called mpr121_touchkey.
config KEYBOARD_IMX
tristate "IMX keypad support"
depends on ARCH_MXC
select INPUT_MATRIXKMAP
help
Enable support for IMX keypad port.
To compile this driver as a module, choose M here: the
module will be called imx_keypad.
config KEYBOARD_NEWTON
tristate "Newton keyboard"
select SERIO
help
Say Y here if you have a Newton keyboard on a serial port.
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
config KEYBOARD_NOMADIK
tristate "ST-Ericsson Nomadik SKE keyboard"
depends on (ARCH_NOMADIK || ARCH_U8500)
select INPUT_MATRIXKMAP
help
Say Y here if you want to use a keypad provided on the SKE controller
used on the Ux500 and Nomadik platforms
To compile this driver as a module, choose M here: the
module will be called nmk-ske-keypad.
config KEYBOARD_TEGRA
tristate "NVIDIA Tegra internal matrix keyboard controller support"
depends on ARCH_TEGRA && OF
select INPUT_MATRIXKMAP
help
Say Y here if you want to use a matrix keyboard connected directly
to the internal keyboard controller on Tegra SoCs.
To compile this driver as a module, choose M here: the
module will be called tegra-kbc.
config KEYBOARD_OPENCORES
tristate "OpenCores Keyboard Controller"
depends on HAS_IOMEM
help
Say Y here if you want to use the OpenCores Keyboard Controller
http://www.opencores.org/project,keyboardcontroller
To compile this driver as a module, choose M here; the
module will be called opencores-kbd.
config KEYBOARD_PXA27x
tristate "PXA27x/PXA3xx keypad support"
depends on PXA27x || PXA3xx || ARCH_MMP
help
Enable support for PXA27x/PXA3xx keypad controller.
To compile this driver as a module, choose M here: the
module will be called pxa27x_keypad.
config KEYBOARD_PXA930_ROTARY
tristate "PXA930/PXA935 Enhanced Rotary Controller Support"
depends on CPU_PXA930 || CPU_PXA935
help
Enable support for PXA930/PXA935 Enhanced Rotary Controller.
To compile this driver as a module, choose M here: the
module will be called pxa930_rotary.
config KEYBOARD_PMIC8XXX
tristate "Qualcomm PMIC8XXX keypad support"
depends on MFD_PM8XXX
select INPUT_MATRIXKMAP
help
Say Y here if you want to enable the driver for the PMIC8XXX
keypad provided as a reference design from Qualcomm. This is intended
to support upto 18x8 matrix based keypad design.
To compile this driver as a module, choose M here: the module will
be called pmic8xxx-keypad.
config KEYBOARD_SAMSUNG
tristate "Samsung keypad support"
depends on HAVE_CLK
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the keypad on your Samsung mobile
device.
To compile this driver as a module, choose M here: the
module will be called samsung-keypad.
config KEYBOARD_GOLDFISH_EVENTS
depends on GOLDFISH
tristate "Generic Input Event device for Goldfish"
help
Say Y here to get an input event device for the Goldfish virtual
device emulator.
To compile this driver as a module, choose M here: the
module will be called goldfish-events.
config KEYBOARD_STOWAWAY
tristate "Stowaway keyboard"
select SERIO
help
Say Y here if you have a Stowaway keyboard on a serial port.
Stowaway compatible keyboards like Dicota Input-PDA keyboard
are also supported by this driver.
To compile this driver as a module, choose M here: the
module will be called stowaway.
config KEYBOARD_SUNKBD
tristate "Sun Type 4 and Type 5 keyboard"
select SERIO
help
Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
connected either to the Sun keyboard connector or to an serial
(RS-232) port via a simple adapter.
To compile this driver as a module, choose M here: the
module will be called sunkbd.
config KEYBOARD_SH_KEYSC
tristate "SuperH KEYSC keypad support"
depends on SUPERH || ARCH_SHMOBILE
help
Say Y here if you want to use a keypad attached to the KEYSC block
on SuperH processors such as sh7722 and sh7343.
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
config KEYBOARD_STMPE
tristate "STMPE keypad support"
depends on MFD_STMPE
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the keypad controller on STMPE I/O
expanders.
To compile this driver as a module, choose M here: the module will be
called stmpe-keypad.
config KEYBOARD_DAVINCI
tristate "TI DaVinci Key Scan"
depends on ARCH_DAVINCI_DM365
help
Say Y to enable keypad module support for the TI DaVinci
platforms (DM365).
To compile this driver as a module, choose M here: the
module will be called davinci_keyscan.
config KEYBOARD_OMAP
tristate "TI OMAP keypad support"
depends on ARCH_OMAP1
select INPUT_MATRIXKMAP
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 KEYBOARD_OMAP4
tristate "TI OMAP4+ keypad support"
depends on ARCH_OMAP2PLUS
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the OMAP4+ keypad.
To compile this driver as a module, choose M here: the
module will be called omap4-keypad.
config KEYBOARD_SPEAR
tristate "ST SPEAR keyboard support"
depends on PLAT_SPEAR
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the SPEAR keyboard.
To compile this driver as a module, choose M here: the
module will be called spear-keboard.
config KEYBOARD_TC3589X
tristate "TC3589X Keypad support"
depends on MFD_TC3589X
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the keypad controller on
TC35892/3 I/O expander.
To compile this driver as a module, choose M here: the
module will be called tc3589x-keypad.
config KEYBOARD_TNETV107X
tristate "TI TNETV107X keypad support"
depends on ARCH_DAVINCI_TNETV107X
select INPUT_MATRIXKMAP
help
Say Y here if you want to use the TNETV107X keypad.
To compile this driver as a module, choose M here: the
module will be called tnetv107x-keypad.
config KEYBOARD_TWL4030
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
depends on TWL4030_CORE
select INPUT_MATRIXKMAP
help
Say Y here if your board use the keypad controller on
TWL4030 family chips. It's safe to say enable this
even on boards that don't use the keypad controller.
To compile this driver as a module, choose M here: the
module will be called twl4030_keypad.
config KEYBOARD_XTKBD
tristate "XT keyboard"
select SERIO
help
Say Y here if you want to use the old IBM PC/XT keyboard (or
compatible) on your system. This is only possible with a
parallel port keyboard adapter, you cannot connect it to the
keyboard port on a PC that runs Linux.
To compile this driver as a module, choose M here: the
module will be called xtkbd.
config KEYBOARD_W90P910
tristate "W90P910 Matrix Keypad support"
depends on ARCH_W90X900
select INPUT_MATRIXKMAP
help
Say Y here to enable the matrix keypad on evaluation board
based on W90P910.
To compile this driver as a module, choose M here: the
module will be called w90p910_keypad.
config KEYBOARD_CROS_EC
tristate "ChromeOS EC keyboard"
select INPUT_MATRIXKMAP
depends on MFD_CROS_EC
help
Say Y here to enable the matrix keyboard used by ChromeOS devices
and implemented on the ChromeOS EC. You must enable one bus option
(MFD_CROS_EC_I2C or MFD_CROS_EC_SPI) to use this.
To compile this driver as a module, choose M here: the
module will be called cros_ec_keyb.
endif

View File

@@ -0,0 +1,58 @@
#
# Makefile for the input core drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
obj-$(CONFIG_KEYBOARD_LPC32XX) += lpc32xx-keys.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o

View File

@@ -0,0 +1,210 @@
/*
* Keypad driver for Analog Devices ADP5520 MFD PMICs
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/mfd/adp5520.h>
#include <linux/slab.h>
struct adp5520_keys {
struct input_dev *input;
struct notifier_block notifier;
struct device *master;
unsigned short keycode[ADP5520_KEYMAPSIZE];
};
static void adp5520_keys_report_event(struct adp5520_keys *dev,
unsigned short keymask, int value)
{
int i;
for (i = 0; i < ADP5520_MAXKEYS; i++)
if (keymask & (1 << i))
input_report_key(dev->input, dev->keycode[i], value);
input_sync(dev->input);
}
static int adp5520_keys_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct adp5520_keys *dev;
uint8_t reg_val_lo, reg_val_hi;
unsigned short keymask;
dev = container_of(nb, struct adp5520_keys, notifier);
if (event & ADP5520_KP_INT) {
adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
keymask = (reg_val_hi << 8) | reg_val_lo;
/* Read twice to clear */
adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
keymask |= (reg_val_hi << 8) | reg_val_lo;
adp5520_keys_report_event(dev, keymask, 1);
}
if (event & ADP5520_KR_INT) {
adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
keymask = (reg_val_hi << 8) | reg_val_lo;
/* Read twice to clear */
adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
keymask |= (reg_val_hi << 8) | reg_val_lo;
adp5520_keys_report_event(dev, keymask, 0);
}
return 0;
}
static int adp5520_keys_probe(struct platform_device *pdev)
{
struct adp5520_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
struct adp5520_keys *dev;
int ret, i;
unsigned char en_mask, ctl_mask = 0;
if (pdev->id != ID_ADP5520) {
dev_err(&pdev->dev, "only ADP5520 supports Keypad\n");
return -EINVAL;
}
if (pdata == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
return -EINVAL;
}
if (!(pdata->rows_en_mask && pdata->cols_en_mask))
return -EINVAL;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&pdev->dev, "failed to alloc memory\n");
return -ENOMEM;
}
input = input_allocate_device();
if (!input) {
ret = -ENOMEM;
goto err;
}
dev->master = pdev->dev.parent;
dev->input = input;
input->name = pdev->name;
input->phys = "adp5520-keys/input0";
input->dev.parent = &pdev->dev;
input_set_drvdata(input, dev);
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x5520;
input->id.version = 0x0001;
input->keycodesize = sizeof(dev->keycode[0]);
input->keycodemax = pdata->keymapsize;
input->keycode = dev->keycode;
memcpy(dev->keycode, pdata->keymap,
pdata->keymapsize * input->keycodesize);
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
__set_bit(dev->keycode[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
ret = input_register_device(input);
if (ret) {
dev_err(&pdev->dev, "unable to register input device\n");
goto err;
}
en_mask = pdata->rows_en_mask | pdata->cols_en_mask;
ret = adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_1, en_mask);
if (en_mask & ADP5520_COL_C3)
ctl_mask |= ADP5520_C3_MODE;
if (en_mask & ADP5520_ROW_R3)
ctl_mask |= ADP5520_R3_MODE;
if (ctl_mask)
ret |= adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
ctl_mask);
ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
pdata->rows_en_mask);
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
ret = -EIO;
goto err1;
}
dev->notifier.notifier_call = adp5520_keys_notifier;
ret = adp5520_register_notifier(dev->master, &dev->notifier,
ADP5520_KP_IEN | ADP5520_KR_IEN);
if (ret) {
dev_err(&pdev->dev, "failed to register notifier\n");
goto err1;
}
platform_set_drvdata(pdev, dev);
return 0;
err1:
input_unregister_device(input);
input = NULL;
err:
input_free_device(input);
kfree(dev);
return ret;
}
static int adp5520_keys_remove(struct platform_device *pdev)
{
struct adp5520_keys *dev = platform_get_drvdata(pdev);
adp5520_unregister_notifier(dev->master, &dev->notifier,
ADP5520_KP_IEN | ADP5520_KR_IEN);
input_unregister_device(dev->input);
kfree(dev);
return 0;
}
static struct platform_driver adp5520_keys_driver = {
.driver = {
.name = "adp5520-keys",
.owner = THIS_MODULE,
},
.probe = adp5520_keys_probe,
.remove = adp5520_keys_remove,
};
module_platform_driver(adp5520_keys_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Keys ADP5520 Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:adp5520-keys");

View File

@@ -0,0 +1,661 @@
/*
* File: drivers/input/keyboard/adp5588_keys.c
* Description: keypad driver for ADP5588 and ADP5587
* I2C QWERTY Keypad and IO Expander
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (C) 2008-2010 Analog Devices Inc.
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/i2c/adp5588.h>
/* Key Event Register xy */
#define KEY_EV_PRESSED (1 << 7)
#define KEY_EV_MASK (0x7F)
#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */
#define KEYP_MAX_EVENT 10
/*
* Early pre 4.0 Silicon required to delay readout by at least 25ms,
* since the Event Counter Register updated 25ms after the interrupt
* asserted.
*/
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
struct adp5588_kpad {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work work;
unsigned long delay;
unsigned short keycode[ADP5588_KEYMAPSIZE];
const struct adp5588_gpi_map *gpimap;
unsigned short gpimapsize;
#ifdef CONFIG_GPIOLIB
unsigned char gpiomap[ADP5588_MAXGPIO];
bool export_gpio;
struct gpio_chip gc;
struct mutex gpio_lock; /* Protect cached dir, dat_out */
u8 dat_out[3];
u8 dir[3];
#endif
};
static int adp5588_read(struct i2c_client *client, u8 reg)
{
int ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
dev_err(&client->dev, "Read Error\n");
return ret;
}
static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
{
return i2c_smbus_write_byte_data(client, reg, val);
}
#ifdef CONFIG_GPIOLIB
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
{
struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit);
}
static void adp5588_gpio_set_value(struct gpio_chip *chip,
unsigned off, int val)
{
struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
mutex_lock(&kpad->gpio_lock);
if (val)
kpad->dat_out[bank] |= bit;
else
kpad->dat_out[bank] &= ~bit;
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
kpad->dat_out[bank]);
mutex_unlock(&kpad->gpio_lock);
}
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
{
struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
int ret;
mutex_lock(&kpad->gpio_lock);
kpad->dir[bank] &= ~bit;
ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
mutex_unlock(&kpad->gpio_lock);
return ret;
}
static int adp5588_gpio_direction_output(struct gpio_chip *chip,
unsigned off, int val)
{
struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
int ret;
mutex_lock(&kpad->gpio_lock);
kpad->dir[bank] |= bit;
if (val)
kpad->dat_out[bank] |= bit;
else
kpad->dat_out[bank] &= ~bit;
ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
kpad->dat_out[bank]);
ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank,
kpad->dir[bank]);
mutex_unlock(&kpad->gpio_lock);
return ret;
}
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
const struct adp5588_kpad_platform_data *pdata)
{
bool pin_used[ADP5588_MAXGPIO];
int n_unused = 0;
int i;
memset(pin_used, 0, sizeof(pin_used));
for (i = 0; i < pdata->rows; i++)
pin_used[i] = true;
for (i = 0; i < pdata->cols; i++)
pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true;
for (i = 0; i < kpad->gpimapsize; i++)
pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true;
for (i = 0; i < ADP5588_MAXGPIO; i++)
if (!pin_used[i])
kpad->gpiomap[n_unused++] = i;
return n_unused;
}
static int adp5588_gpio_add(struct adp5588_kpad *kpad)
{
struct device *dev = &kpad->client->dev;
const struct adp5588_kpad_platform_data *pdata = dev->platform_data;
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
int i, error;
if (!gpio_data)
return 0;
kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata);
if (kpad->gc.ngpio == 0) {
dev_info(dev, "No unused gpios left to export\n");
return 0;
}
kpad->export_gpio = true;
kpad->gc.direction_input = adp5588_gpio_direction_input;
kpad->gc.direction_output = adp5588_gpio_direction_output;
kpad->gc.get = adp5588_gpio_get_value;
kpad->gc.set = adp5588_gpio_set_value;
kpad->gc.can_sleep = 1;
kpad->gc.base = gpio_data->gpio_start;
kpad->gc.label = kpad->client->name;
kpad->gc.owner = THIS_MODULE;
kpad->gc.names = gpio_data->names;
mutex_init(&kpad->gpio_lock);
error = gpiochip_add(&kpad->gc);
if (error) {
dev_err(dev, "gpiochip_add failed, err: %d\n", error);
return error;
}
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
kpad->dat_out[i] = adp5588_read(kpad->client,
GPIO_DAT_OUT1 + i);
kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i);
}
if (gpio_data->setup) {
error = gpio_data->setup(kpad->client,
kpad->gc.base, kpad->gc.ngpio,
gpio_data->context);
if (error)
dev_warn(dev, "setup failed, %d\n", error);
}
return 0;
}
static void adp5588_gpio_remove(struct adp5588_kpad *kpad)
{
struct device *dev = &kpad->client->dev;
const struct adp5588_kpad_platform_data *pdata = dev->platform_data;
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
int error;
if (!kpad->export_gpio)
return;
if (gpio_data->teardown) {
error = gpio_data->teardown(kpad->client,
kpad->gc.base, kpad->gc.ngpio,
gpio_data->context);
if (error)
dev_warn(dev, "teardown failed %d\n", error);
}
error = gpiochip_remove(&kpad->gc);
if (error)
dev_warn(dev, "gpiochip_remove failed %d\n", error);
}
#else
static inline int adp5588_gpio_add(struct adp5588_kpad *kpad)
{
return 0;
}
static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad)
{
}
#endif
static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt)
{
int i, j;
for (i = 0; i < ev_cnt; i++) {
int key = adp5588_read(kpad->client, Key_EVENTA + i);
int key_val = key & KEY_EV_MASK;
if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
for (j = 0; j < kpad->gpimapsize; j++) {
if (key_val == kpad->gpimap[j].pin) {
input_report_switch(kpad->input,
kpad->gpimap[j].sw_evt,
key & KEY_EV_PRESSED);
break;
}
}
} else {
input_report_key(kpad->input,
kpad->keycode[key_val - 1],
key & KEY_EV_PRESSED);
}
}
}
static void adp5588_work(struct work_struct *work)
{
struct adp5588_kpad *kpad = container_of(work,
struct adp5588_kpad, work.work);
struct i2c_client *client = kpad->client;
int status, ev_cnt;
status = adp5588_read(client, INT_STAT);
if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */
dev_err(&client->dev, "Event Overflow Error\n");
if (status & ADP5588_KE_INT) {
ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & ADP5588_KEC;
if (ev_cnt) {
adp5588_report_events(kpad, ev_cnt);
input_sync(kpad->input);
}
}
adp5588_write(client, INT_STAT, status); /* Status is W1C */
}
static irqreturn_t adp5588_irq(int irq, void *handle)
{
struct adp5588_kpad *kpad = handle;
/*
* use keventd context to read the event fifo registers
* Schedule readout at least 25ms after notification for
* REVID < 4
*/
schedule_delayed_work(&kpad->work, kpad->delay);
return IRQ_HANDLED;
}
static int adp5588_setup(struct i2c_client *client)
{
const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
int i, ret;
unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));
ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
if (pdata->en_keylock) {
ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1);
ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2);
ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
}
for (i = 0; i < KEYP_MAX_EVENT; i++)
ret |= adp5588_read(client, Key_EVENTA);
for (i = 0; i < pdata->gpimapsize; i++) {
unsigned short pin = pdata->gpimap[i].pin;
if (pin <= GPI_PIN_ROW_END) {
evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE));
} else {
evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF);
evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8);
}
}
if (pdata->gpimapsize) {
ret |= adp5588_write(client, GPI_EM1, evt_mode1);
ret |= adp5588_write(client, GPI_EM2, evt_mode2);
ret |= adp5588_write(client, GPI_EM3, evt_mode3);
}
if (gpio_data) {
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
int pull_mask = gpio_data->pullup_dis_mask;
ret |= adp5588_write(client, GPIO_PULL1 + i,
(pull_mask >> (8 * i)) & 0xFF);
}
}
ret |= adp5588_write(client, INT_STAT,
ADP5588_CMP2_INT | ADP5588_CMP1_INT |
ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT |
ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */
ret |= adp5588_write(client, CFG, ADP5588_INT_CFG |
ADP5588_OVR_FLOW_IEN |
ADP5588_KE_IEN);
if (ret < 0) {
dev_err(&client->dev, "Write Error\n");
return ret;
}
return 0;
}
static void adp5588_report_switch_state(struct adp5588_kpad *kpad)
{
int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1);
int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2);
int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3);
int gpi_stat_tmp, pin_loc;
int i;
for (i = 0; i < kpad->gpimapsize; i++) {
unsigned short pin = kpad->gpimap[i].pin;
if (pin <= GPI_PIN_ROW_END) {
gpi_stat_tmp = gpi_stat1;
pin_loc = pin - GPI_PIN_ROW_BASE;
} else if ((pin - GPI_PIN_COL_BASE) < 8) {
gpi_stat_tmp = gpi_stat2;
pin_loc = pin - GPI_PIN_COL_BASE;
} else {
gpi_stat_tmp = gpi_stat3;
pin_loc = pin - GPI_PIN_COL_BASE - 8;
}
if (gpi_stat_tmp < 0) {
dev_err(&kpad->client->dev,
"Can't read GPIO_DAT_STAT switch %d default to OFF\n",
pin);
gpi_stat_tmp = 0;
}
input_report_switch(kpad->input,
kpad->gpimap[i].sw_evt,
!(gpi_stat_tmp & (1 << pin_loc)));
}
input_sync(kpad->input);
}
static int adp5588_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adp5588_kpad *kpad;
const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
struct input_dev *input;
unsigned int revid;
int ret, i;
int error;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
return -EIO;
}
if (!pdata) {
dev_err(&client->dev, "no platform data?\n");
return -EINVAL;
}
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
return -EINVAL;
}
if (pdata->keymapsize != ADP5588_KEYMAPSIZE) {
dev_err(&client->dev, "invalid keymapsize\n");
return -EINVAL;
}
if (!pdata->gpimap && pdata->gpimapsize) {
dev_err(&client->dev, "invalid gpimap from pdata\n");
return -EINVAL;
}
if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) {
dev_err(&client->dev, "invalid gpimapsize\n");
return -EINVAL;
}
for (i = 0; i < pdata->gpimapsize; i++) {
unsigned short pin = pdata->gpimap[i].pin;
if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) {
dev_err(&client->dev, "invalid gpi pin data\n");
return -EINVAL;
}
if (pin <= GPI_PIN_ROW_END) {
if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) {
dev_err(&client->dev, "invalid gpi row data\n");
return -EINVAL;
}
} else {
if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) {
dev_err(&client->dev, "invalid gpi col data\n");
return -EINVAL;
}
}
}
if (!client->irq) {
dev_err(&client->dev, "no IRQ?\n");
return -EINVAL;
}
kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
input = input_allocate_device();
if (!kpad || !input) {
error = -ENOMEM;
goto err_free_mem;
}
kpad->client = client;
kpad->input = input;
INIT_DELAYED_WORK(&kpad->work, adp5588_work);
ret = adp5588_read(client, DEV_ID);
if (ret < 0) {
error = ret;
goto err_free_mem;
}
revid = (u8) ret & ADP5588_DEVICE_ID_MASK;
if (WA_DELAYED_READOUT_REVID(revid))
kpad->delay = msecs_to_jiffies(30);
input->name = client->name;
input->phys = "adp5588-keys/input0";
input->dev.parent = &client->dev;
input_set_drvdata(input, kpad);
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = revid;
input->keycodesize = sizeof(kpad->keycode[0]);
input->keycodemax = pdata->keymapsize;
input->keycode = kpad->keycode;
memcpy(kpad->keycode, pdata->keymap,
pdata->keymapsize * input->keycodesize);
kpad->gpimap = pdata->gpimap;
kpad->gpimapsize = pdata->gpimapsize;
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
if (kpad->gpimapsize)
__set_bit(EV_SW, input->evbit);
for (i = 0; i < kpad->gpimapsize; i++)
__set_bit(kpad->gpimap[i].sw_evt, input->swbit);
error = input_register_device(input);
if (error) {
dev_err(&client->dev, "unable to register input device\n");
goto err_free_mem;
}
error = request_irq(client->irq, adp5588_irq,
IRQF_TRIGGER_FALLING,
client->dev.driver->name, kpad);
if (error) {
dev_err(&client->dev, "irq %d busy?\n", client->irq);
goto err_unreg_dev;
}
error = adp5588_setup(client);
if (error)
goto err_free_irq;
if (kpad->gpimapsize)
adp5588_report_switch_state(kpad);
error = adp5588_gpio_add(kpad);
if (error)
goto err_free_irq;
device_init_wakeup(&client->dev, 1);
i2c_set_clientdata(client, kpad);
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
return 0;
err_free_irq:
free_irq(client->irq, kpad);
err_unreg_dev:
input_unregister_device(input);
input = NULL;
err_free_mem:
input_free_device(input);
kfree(kpad);
return error;
}
static int adp5588_remove(struct i2c_client *client)
{
struct adp5588_kpad *kpad = i2c_get_clientdata(client);
adp5588_write(client, CFG, 0);
free_irq(client->irq, kpad);
cancel_delayed_work_sync(&kpad->work);
input_unregister_device(kpad->input);
adp5588_gpio_remove(kpad);
kfree(kpad);
return 0;
}
#ifdef CONFIG_PM
static int adp5588_suspend(struct device *dev)
{
struct adp5588_kpad *kpad = dev_get_drvdata(dev);
struct i2c_client *client = kpad->client;
disable_irq(client->irq);
cancel_delayed_work_sync(&kpad->work);
if (device_may_wakeup(&client->dev))
enable_irq_wake(client->irq);
return 0;
}
static int adp5588_resume(struct device *dev)
{
struct adp5588_kpad *kpad = dev_get_drvdata(dev);
struct i2c_client *client = kpad->client;
if (device_may_wakeup(&client->dev))
disable_irq_wake(client->irq);
enable_irq(client->irq);
return 0;
}
static const struct dev_pm_ops adp5588_dev_pm_ops = {
.suspend = adp5588_suspend,
.resume = adp5588_resume,
};
#endif
static const struct i2c_device_id adp5588_id[] = {
{ "adp5588-keys", 0 },
{ "adp5587-keys", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adp5588_id);
static struct i2c_driver adp5588_driver = {
.driver = {
.name = KBUILD_MODNAME,
#ifdef CONFIG_PM
.pm = &adp5588_dev_pm_ops,
#endif
},
.probe = adp5588_probe,
.remove = adp5588_remove,
.id_table = adp5588_id,
};
module_i2c_driver(adp5588_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADP5588/87 Keypad driver");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,265 @@
/*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Hamish Macdonald
*/
/*
* Amiga keyboard driver for Linux/m68k
*/
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/keyboard.h>
#include <linux/platform_device.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Amiga keyboard driver");
MODULE_LICENSE("GPL");
static unsigned char amikbd_keycode[0x78] __initdata = {
[0] = KEY_GRAVE,
[1] = KEY_1,
[2] = KEY_2,
[3] = KEY_3,
[4] = KEY_4,
[5] = KEY_5,
[6] = KEY_6,
[7] = KEY_7,
[8] = KEY_8,
[9] = KEY_9,
[10] = KEY_0,
[11] = KEY_MINUS,
[12] = KEY_EQUAL,
[13] = KEY_BACKSLASH,
[15] = KEY_KP0,
[16] = KEY_Q,
[17] = KEY_W,
[18] = KEY_E,
[19] = KEY_R,
[20] = KEY_T,
[21] = KEY_Y,
[22] = KEY_U,
[23] = KEY_I,
[24] = KEY_O,
[25] = KEY_P,
[26] = KEY_LEFTBRACE,
[27] = KEY_RIGHTBRACE,
[29] = KEY_KP1,
[30] = KEY_KP2,
[31] = KEY_KP3,
[32] = KEY_A,
[33] = KEY_S,
[34] = KEY_D,
[35] = KEY_F,
[36] = KEY_G,
[37] = KEY_H,
[38] = KEY_J,
[39] = KEY_K,
[40] = KEY_L,
[41] = KEY_SEMICOLON,
[42] = KEY_APOSTROPHE,
[43] = KEY_BACKSLASH,
[45] = KEY_KP4,
[46] = KEY_KP5,
[47] = KEY_KP6,
[48] = KEY_102ND,
[49] = KEY_Z,
[50] = KEY_X,
[51] = KEY_C,
[52] = KEY_V,
[53] = KEY_B,
[54] = KEY_N,
[55] = KEY_M,
[56] = KEY_COMMA,
[57] = KEY_DOT,
[58] = KEY_SLASH,
[60] = KEY_KPDOT,
[61] = KEY_KP7,
[62] = KEY_KP8,
[63] = KEY_KP9,
[64] = KEY_SPACE,
[65] = KEY_BACKSPACE,
[66] = KEY_TAB,
[67] = KEY_KPENTER,
[68] = KEY_ENTER,
[69] = KEY_ESC,
[70] = KEY_DELETE,
[74] = KEY_KPMINUS,
[76] = KEY_UP,
[77] = KEY_DOWN,
[78] = KEY_RIGHT,
[79] = KEY_LEFT,
[80] = KEY_F1,
[81] = KEY_F2,
[82] = KEY_F3,
[83] = KEY_F4,
[84] = KEY_F5,
[85] = KEY_F6,
[86] = KEY_F7,
[87] = KEY_F8,
[88] = KEY_F9,
[89] = KEY_F10,
[90] = KEY_KPLEFTPAREN,
[91] = KEY_KPRIGHTPAREN,
[92] = KEY_KPSLASH,
[93] = KEY_KPASTERISK,
[94] = KEY_KPPLUS,
[95] = KEY_HELP,
[96] = KEY_LEFTSHIFT,
[97] = KEY_RIGHTSHIFT,
[98] = KEY_CAPSLOCK,
[99] = KEY_LEFTCTRL,
[100] = KEY_LEFTALT,
[101] = KEY_RIGHTALT,
[102] = KEY_LEFTMETA,
[103] = KEY_RIGHTMETA
};
static const char *amikbd_messages[8] = {
[0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
[1] = KERN_WARNING "amikbd: keyboard lost sync\n",
[2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
[3] = KERN_WARNING "amikbd: keyboard controller failure\n",
[4] = KERN_ERR "amikbd: keyboard selftest failure\n",
[5] = KERN_INFO "amikbd: initiate power-up key stream\n",
[6] = KERN_INFO "amikbd: terminate power-up key stream\n",
[7] = KERN_WARNING "amikbd: keyboard interrupt\n"
};
static irqreturn_t amikbd_interrupt(int irq, void *data)
{
struct input_dev *dev = data;
unsigned char scancode, down;
scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */
ciaa.cra |= 0x40; /* switch SP pin to output for handshake */
udelay(85); /* wait until 85 us have expired */
ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */
down = !(scancode & 1); /* lowest bit is release bit */
scancode >>= 1;
if (scancode < 0x78) { /* scancodes < 0x78 are keys */
if (scancode == 98) { /* CapsLock is a toggle switch key on Amiga */
input_report_key(dev, scancode, 1);
input_report_key(dev, scancode, 0);
} else {
input_report_key(dev, scancode, down);
}
input_sync(dev);
} else /* scancodes >= 0x78 are error codes */
printk(amikbd_messages[scancode - 0x78]);
return IRQ_HANDLED;
}
static int __init amikbd_probe(struct platform_device *pdev)
{
struct input_dev *dev;
int i, j, err;
dev = input_allocate_device();
if (!dev) {
dev_err(&pdev->dev, "Not enough memory for input device\n");
return -ENOMEM;
}
dev->name = pdev->name;
dev->phys = "amikbd/input0";
dev->id.bustype = BUS_AMIGA;
dev->id.vendor = 0x0001;
dev->id.product = 0x0001;
dev->id.version = 0x0100;
dev->dev.parent = &pdev->dev;
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
for (i = 0; i < 0x78; i++)
set_bit(i, dev->keybit);
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
static u_short temp_map[NR_KEYS] __initdata;
if (!key_maps[i])
continue;
memset(temp_map, 0, sizeof(temp_map));
for (j = 0; j < 0x78; j++) {
if (!amikbd_keycode[j])
continue;
temp_map[j] = key_maps[i][amikbd_keycode[j]];
}
for (j = 0; j < NR_KEYS; j++) {
if (!temp_map[j])
temp_map[j] = 0xf200;
}
memcpy(key_maps[i], temp_map, sizeof(temp_map));
}
ciaa.cra &= ~0x41; /* serial data in, turn off TA */
err = request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd",
dev);
if (err)
goto fail2;
err = input_register_device(dev);
if (err)
goto fail3;
platform_set_drvdata(pdev, dev);
return 0;
fail3: free_irq(IRQ_AMIGA_CIAA_SP, dev);
fail2: input_free_device(dev);
return err;
}
static int __exit amikbd_remove(struct platform_device *pdev)
{
struct input_dev *dev = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
free_irq(IRQ_AMIGA_CIAA_SP, dev);
input_unregister_device(dev);
return 0;
}
static struct platform_driver amikbd_driver = {
.remove = __exit_p(amikbd_remove),
.driver = {
.name = "amiga-keyboard",
.owner = THIS_MODULE,
},
};
module_platform_driver_probe(amikbd_driver, amikbd_probe);
MODULE_ALIAS("platform:amiga-keyboard");

View File

@@ -0,0 +1,269 @@
/*
* atakbd.c
*
* Copyright (c) 2005 Michael Schmitz
*
* Based on amikbd.c, which is
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Hamish Macdonald
*/
/*
* Atari keyboard driver for Linux/m68k
*
* The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
* (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
* interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
* This driver only deals with handing key events off to the input layer.
*/
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/atariints.h>
#include <asm/atarihw.h>
#include <asm/atarikb.h>
#include <asm/irq.h>
MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
MODULE_DESCRIPTION("Atari keyboard driver");
MODULE_LICENSE("GPL");
/*
0x47: KP_7 71
0x48: KP_8 72
0x49: KP_9 73
0x62: KP_/ 98
0x4b: KP_4 75
0x4c: KP_5 76
0x4d: KP_6 77
0x37: KP_* 55
0x4f: KP_1 79
0x50: KP_2 80
0x51: KP_3 81
0x4a: KP_- 74
0x52: KP_0 82
0x53: KP_. 83
0x4e: KP_+ 78
0x67: Up 103
0x6c: Down 108
0x69: Left 105
0x6a: Right 106
*/
static unsigned char atakbd_keycode[0x72] = { /* American layout */
[0] = KEY_GRAVE,
[1] = KEY_ESC,
[2] = KEY_1,
[3] = KEY_2,
[4] = KEY_3,
[5] = KEY_4,
[6] = KEY_5,
[7] = KEY_6,
[8] = KEY_7,
[9] = KEY_8,
[10] = KEY_9,
[11] = KEY_0,
[12] = KEY_MINUS,
[13] = KEY_EQUAL,
[14] = KEY_BACKSPACE,
[15] = KEY_TAB,
[16] = KEY_Q,
[17] = KEY_W,
[18] = KEY_E,
[19] = KEY_R,
[20] = KEY_T,
[21] = KEY_Y,
[22] = KEY_U,
[23] = KEY_I,
[24] = KEY_O,
[25] = KEY_P,
[26] = KEY_LEFTBRACE,
[27] = KEY_RIGHTBRACE,
[28] = KEY_ENTER,
[29] = KEY_LEFTCTRL,
[30] = KEY_A,
[31] = KEY_S,
[32] = KEY_D,
[33] = KEY_F,
[34] = KEY_G,
[35] = KEY_H,
[36] = KEY_J,
[37] = KEY_K,
[38] = KEY_L,
[39] = KEY_SEMICOLON,
[40] = KEY_APOSTROPHE,
[41] = KEY_BACKSLASH, /* FIXME, '#' */
[42] = KEY_LEFTSHIFT,
[43] = KEY_GRAVE, /* FIXME: '~' */
[44] = KEY_Z,
[45] = KEY_X,
[46] = KEY_C,
[47] = KEY_V,
[48] = KEY_B,
[49] = KEY_N,
[50] = KEY_M,
[51] = KEY_COMMA,
[52] = KEY_DOT,
[53] = KEY_SLASH,
[54] = KEY_RIGHTSHIFT,
[55] = KEY_KPASTERISK,
[56] = KEY_LEFTALT,
[57] = KEY_SPACE,
[58] = KEY_CAPSLOCK,
[59] = KEY_F1,
[60] = KEY_F2,
[61] = KEY_F3,
[62] = KEY_F4,
[63] = KEY_F5,
[64] = KEY_F6,
[65] = KEY_F7,
[66] = KEY_F8,
[67] = KEY_F9,
[68] = KEY_F10,
[69] = KEY_ESC,
[70] = KEY_DELETE,
[71] = KEY_KP7,
[72] = KEY_KP8,
[73] = KEY_KP9,
[74] = KEY_KPMINUS,
[75] = KEY_KP4,
[76] = KEY_KP5,
[77] = KEY_KP6,
[78] = KEY_KPPLUS,
[79] = KEY_KP1,
[80] = KEY_KP2,
[81] = KEY_KP3,
[82] = KEY_KP0,
[83] = KEY_KPDOT,
[90] = KEY_KPLEFTPAREN,
[91] = KEY_KPRIGHTPAREN,
[92] = KEY_KPASTERISK, /* FIXME */
[93] = KEY_KPASTERISK,
[94] = KEY_KPPLUS,
[95] = KEY_HELP,
[96] = KEY_BACKSLASH, /* FIXME: '<' */
[97] = KEY_KPASTERISK, /* FIXME */
[98] = KEY_KPSLASH,
[99] = KEY_KPLEFTPAREN,
[100] = KEY_KPRIGHTPAREN,
[101] = KEY_KPSLASH,
[102] = KEY_KPASTERISK,
[103] = KEY_UP,
[104] = KEY_KPASTERISK, /* FIXME */
[105] = KEY_LEFT,
[106] = KEY_RIGHT,
[107] = KEY_KPASTERISK, /* FIXME */
[108] = KEY_DOWN,
[109] = KEY_KPASTERISK, /* FIXME */
[110] = KEY_KPASTERISK, /* FIXME */
[111] = KEY_KPASTERISK, /* FIXME */
[112] = KEY_KPASTERISK, /* FIXME */
[113] = KEY_KPASTERISK /* FIXME */
};
static struct input_dev *atakbd_dev;
static void atakbd_interrupt(unsigned char scancode, char down)
{
if (scancode < 0x72) { /* scancodes < 0xf2 are keys */
// report raw events here?
scancode = atakbd_keycode[scancode];
if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
input_report_key(atakbd_dev, scancode, 1);
input_report_key(atakbd_dev, scancode, 0);
input_sync(atakbd_dev);
} else {
input_report_key(atakbd_dev, scancode, down);
input_sync(atakbd_dev);
}
} else /* scancodes >= 0xf2 are mouse data, most likely */
printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode);
return;
}
static int __init atakbd_init(void)
{
int i, error;
if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
return -ENODEV;
// need to init core driver if not already done so
error = atari_keyb_init();
if (error)
return error;
atakbd_dev = input_allocate_device();
if (!atakbd_dev)
return -ENOMEM;
atakbd_dev->name = "Atari Keyboard";
atakbd_dev->phys = "atakbd/input0";
atakbd_dev->id.bustype = BUS_HOST;
atakbd_dev->id.vendor = 0x0001;
atakbd_dev->id.product = 0x0001;
atakbd_dev->id.version = 0x0100;
atakbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
atakbd_dev->keycode = atakbd_keycode;
atakbd_dev->keycodesize = sizeof(unsigned char);
atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode);
for (i = 1; i < 0x72; i++) {
set_bit(atakbd_keycode[i], atakbd_dev->keybit);
}
/* error check */
error = input_register_device(atakbd_dev);
if (error) {
input_free_device(atakbd_dev);
return error;
}
atari_input_keyboard_interrupt_hook = atakbd_interrupt;
return 0;
}
static void __exit atakbd_exit(void)
{
atari_input_keyboard_interrupt_hook = NULL;
input_unregister_device(atakbd_dev);
}
module_init(atakbd_init);
module_exit(atakbd_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,402 @@
/*
* File: drivers/input/keyboard/bf54x-keys.c
* Based on:
* Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
*
* Created:
* Description: keypad driver for Analog Devices Blackfin BF54x Processors
*
*
* Modified:
* Copyright 2007-2008 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <asm/portmux.h>
#include <mach/bf54x_keys.h>
#define DRV_NAME "bf54x-keys"
#define TIME_SCALE 100 /* 100 ns */
#define MAX_MULT (0xFF * TIME_SCALE)
#define MAX_RC 8 /* Max Row/Col */
static const u16 per_rows[] = {
P_KEY_ROW7,
P_KEY_ROW6,
P_KEY_ROW5,
P_KEY_ROW4,
P_KEY_ROW3,
P_KEY_ROW2,
P_KEY_ROW1,
P_KEY_ROW0,
0
};
static const u16 per_cols[] = {
P_KEY_COL7,
P_KEY_COL6,
P_KEY_COL5,
P_KEY_COL4,
P_KEY_COL3,
P_KEY_COL2,
P_KEY_COL1,
P_KEY_COL0,
0
};
struct bf54x_kpad {
struct input_dev *input;
int irq;
unsigned short lastkey;
unsigned short *keycode;
struct timer_list timer;
unsigned int keyup_test_jiffies;
unsigned short kpad_msel;
unsigned short kpad_prescale;
unsigned short kpad_ctl;
};
static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
struct input_dev *input, u16 keyident)
{
u16 i;
for (i = 0; i < input->keycodemax; i++)
if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
return bf54x_kpad->keycode[i];
return -1;
}
static inline void bfin_keycodecpy(unsigned short *keycode,
const unsigned int *pdata_kc,
unsigned short keymapsize)
{
unsigned int i;
for (i = 0; i < keymapsize; i++) {
keycode[i] = pdata_kc[i] & 0xffff;
keycode[i + keymapsize] = pdata_kc[i] >> 16;
}
}
static inline u16 bfin_kpad_get_prescale(u32 timescale)
{
u32 sclk = get_sclk();
return ((((sclk / 1000) * timescale) / 1024) - 1);
}
static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
{
return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
}
static inline void bfin_kpad_clear_irq(void)
{
bfin_write_KPAD_STAT(0xFFFF);
bfin_write_KPAD_ROWCOL(0xFFFF);
}
static void bfin_kpad_timer(unsigned long data)
{
struct platform_device *pdev = (struct platform_device *) data;
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
/* Try again later */
mod_timer(&bf54x_kpad->timer,
jiffies + bf54x_kpad->keyup_test_jiffies);
return;
}
input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
input_sync(bf54x_kpad->input);
/* Clear IRQ Status */
bfin_kpad_clear_irq();
enable_irq(bf54x_kpad->irq);
}
static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
struct input_dev *input = bf54x_kpad->input;
int key;
u16 rowcol = bfin_read_KPAD_ROWCOL();
key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
input_report_key(input, key, 1);
input_sync(input);
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
disable_irq_nosync(bf54x_kpad->irq);
bf54x_kpad->lastkey = key;
mod_timer(&bf54x_kpad->timer,
jiffies + bf54x_kpad->keyup_test_jiffies);
} else {
input_report_key(input, key, 0);
input_sync(input);
bfin_kpad_clear_irq();
}
return IRQ_HANDLED;
}
static int bfin_kpad_probe(struct platform_device *pdev)
{
struct bf54x_kpad *bf54x_kpad;
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
int i, error;
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
return -EINVAL;
}
if (!pdata->keymapsize ||
pdata->keymapsize > (pdata->rows * pdata->cols)) {
dev_err(&pdev->dev, "invalid keymapsize\n");
return -EINVAL;
}
bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
if (!bf54x_kpad)
return -ENOMEM;
platform_set_drvdata(pdev, bf54x_kpad);
/* Allocate memory for keymap followed by private LUT */
bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
sizeof(unsigned short) * 2, GFP_KERNEL);
if (!bf54x_kpad->keycode) {
error = -ENOMEM;
goto out;
}
if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
!pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
dev_warn(&pdev->dev,
"invalid platform debounce/columndrive time\n");
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
} else {
bfin_write_KPAD_MSEL(
((pdata->debounce_time / TIME_SCALE)
& DBON_SCALE) |
(((pdata->coldrive_time / TIME_SCALE) << 8)
& COLDRV_SCALE));
}
if (!pdata->keyup_test_interval)
bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
else
bf54x_kpad->keyup_test_jiffies =
msecs_to_jiffies(pdata->keyup_test_interval);
if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
DRV_NAME)) {
dev_err(&pdev->dev, "requesting peripherals failed\n");
error = -EFAULT;
goto out0;
}
if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
DRV_NAME)) {
dev_err(&pdev->dev, "requesting peripherals failed\n");
error = -EFAULT;
goto out1;
}
bf54x_kpad->irq = platform_get_irq(pdev, 0);
if (bf54x_kpad->irq < 0) {
error = -ENODEV;
goto out2;
}
error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
0, DRV_NAME, pdev);
if (error) {
dev_err(&pdev->dev, "unable to claim irq %d\n",
bf54x_kpad->irq);
goto out2;
}
input = input_allocate_device();
if (!input) {
error = -ENOMEM;
goto out3;
}
bf54x_kpad->input = input;
input->name = pdev->name;
input->phys = "bf54x-keys/input0";
input->dev.parent = &pdev->dev;
input_set_drvdata(input, bf54x_kpad);
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->keycodesize = sizeof(unsigned short);
input->keycodemax = pdata->keymapsize;
input->keycode = bf54x_kpad->keycode;
bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "unable to register input device\n");
goto out4;
}
/* Init Keypad Key Up/Release test timer */
setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
(((pdata->rows - 1) << 10) & KPAD_ROWEN) |
(2 & KPAD_IRQMODE));
bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
device_init_wakeup(&pdev->dev, 1);
return 0;
out4:
input_free_device(input);
out3:
free_irq(bf54x_kpad->irq, pdev);
out2:
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
out1:
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
out0:
kfree(bf54x_kpad->keycode);
out:
kfree(bf54x_kpad);
platform_set_drvdata(pdev, NULL);
return error;
}
static int bfin_kpad_remove(struct platform_device *pdev)
{
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
del_timer_sync(&bf54x_kpad->timer);
free_irq(bf54x_kpad->irq, pdev);
input_unregister_device(bf54x_kpad->input);
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
kfree(bf54x_kpad->keycode);
kfree(bf54x_kpad);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
{
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(bf54x_kpad->irq);
return 0;
}
static int bfin_kpad_resume(struct platform_device *pdev)
{
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(bf54x_kpad->irq);
return 0;
}
#else
# define bfin_kpad_suspend NULL
# define bfin_kpad_resume NULL
#endif
static struct platform_driver bfin_kpad_device_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = bfin_kpad_probe,
.remove = bfin_kpad_remove,
.suspend = bfin_kpad_suspend,
.resume = bfin_kpad_resume,
};
module_platform_driver(bfin_kpad_device_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
MODULE_ALIAS("platform:bf54x-keys");

View File

@@ -0,0 +1,365 @@
/*
* ChromeOS EC keyboard driver
*
* Copyright (C) 2012 Google, Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
* This driver uses the Chrome OS EC byte-level message-based protocol for
* communicating the keyboard state (which keys are pressed) from a keyboard EC
* to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
* but everything else (including deghosting) is done here. The main
* motivation for this is to keep the EC firmware as simple as possible, since
* it cannot be easily upgraded and EC flash/IRAM space is relatively
* expensive.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/input/matrix_keypad.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
/*
* @rows: Number of rows in the keypad
* @cols: Number of columns in the keypad
* @row_shift: log2 or number of rows, rounded up
* @keymap_data: Matrix keymap data used to convert to keyscan values
* @ghost_filter: true to enable the matrix key-ghosting filter
* @old_kb_state: bitmap of keys pressed last scan
* @dev: Device pointer
* @idev: Input device
* @ec: Top level ChromeOS device to use to talk to EC
*/
struct cros_ec_keyb {
unsigned int rows;
unsigned int cols;
int row_shift;
const struct matrix_keymap_data *keymap_data;
bool ghost_filter;
uint8_t *old_kb_state;
struct device *dev;
struct input_dev *idev;
struct cros_ec_device *ec;
};
static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev,
uint8_t *buf, int row)
{
int pressed_in_row = 0;
int row_has_teeth = 0;
int col, mask;
mask = 1 << row;
for (col = 0; col < ckdev->cols; col++) {
if (buf[col] & mask) {
pressed_in_row++;
row_has_teeth |= buf[col] & ~mask;
if (pressed_in_row > 1 && row_has_teeth) {
/* ghosting */
dev_dbg(ckdev->dev,
"ghost found at: r%d c%d, pressed %d, teeth 0x%x\n",
row, col, pressed_in_row,
row_has_teeth);
return true;
}
}
}
return false;
}
/*
* Returns true when there is at least one combination of pressed keys that
* results in ghosting.
*/
static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf)
{
int row;
/*
* Ghosting happens if for any pressed key X there are other keys
* pressed both in the same row and column of X as, for instance,
* in the following diagram:
*
* . . Y . g .
* . . . . . .
* . . . . . .
* . . X . Z .
*
* In this case only X, Y, and Z are pressed, but g appears to be
* pressed too (see Wikipedia).
*
* We can detect ghosting in a single pass (*) over the keyboard state
* by maintaining two arrays. pressed_in_row counts how many pressed
* keys we have found in a row. row_has_teeth is true if any of the
* pressed keys for this row has other pressed keys in its column. If
* at any point of the scan we find that a row has multiple pressed
* keys, and at least one of them is at the intersection with a column
* with multiple pressed keys, we're sure there is ghosting.
* Conversely, if there is ghosting, we will detect such situation for
* at least one key during the pass.
*
* (*) This looks linear in the number of keys, but it's not. We can
* cheat because the number of rows is small.
*/
for (row = 0; row < ckdev->rows; row++)
if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row))
return true;
return false;
}
/*
* Compares the new keyboard state to the old one and produces key
* press/release events accordingly. The keyboard state is 13 bytes (one byte
* per column)
*/
static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
uint8_t *kb_state, int len)
{
struct input_dev *idev = ckdev->idev;
struct cros_ec_device *ec = ckdev->ec;
int col, row;
int new_state;
int old_state;
int num_cols;
num_cols = len;
if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) {
/*
* Simple-minded solution: ignore this state. The obvious
* improvement is to only ignore changes to keys involved in
* the ghosting, but process the other changes.
*/
dev_dbg(ckdev->dev, "ghosting found\n");
return;
}
for (col = 0; col < ckdev->cols; col++) {
for (row = 0; row < ckdev->rows; row++) {
int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
const unsigned short *keycodes = idev->keycode;
int code;
code = keycodes[pos];
new_state = kb_state[col] & (1 << row);
old_state = ckdev->old_kb_state[col] & (1 << row);
if (new_state != old_state) {
dev_dbg(ckdev->dev,
"changed: [r%d c%d]: byte %02x\n",
row, col, new_state);
/* Change to power supply status */
if ((ec->charger && code == KEY_BATTERY)) {
power_supply_changed(ec->charger);
continue;
}
input_report_key(idev, code, new_state);
}
}
ckdev->old_kb_state[col] = kb_state[col];
}
input_sync(ckdev->idev);
}
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
{
int ret;
struct cros_ec_command msg = {
.version = 0,
.command = EC_CMD_MKBP_STATE,
.outdata = NULL,
.outsize = 0,
.indata = kb_state,
.insize = ckdev->cols,
};
ret = cros_ec_cmd_xfer(ckdev->ec, &msg);
/* FIXME: This assumes msg.result == EC_RES_SUCCESS */
return ret;
}
static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
{
struct cros_ec_keyb *ckdev = data;
struct cros_ec_device *ec = ckdev->ec;
int ret;
uint8_t kb_state[ckdev->cols];
if (device_may_wakeup(ec->dev))
pm_wakeup_event(ec->dev, 0);
ret = cros_ec_keyb_get_state(ckdev, kb_state);
if (ret >= 0)
cros_ec_keyb_process(ckdev, kb_state, ret);
else
dev_err(ec->dev, "failed to get keyboard state: %d\n", ret);
return IRQ_HANDLED;
}
static int cros_ec_keyb_open(struct input_dev *dev)
{
struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
struct cros_ec_device *ec = ckdev->ec;
return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"cros_ec_keyb", ckdev);
}
static void cros_ec_keyb_close(struct input_dev *dev)
{
struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
struct cros_ec_device *ec = ckdev->ec;
free_irq(ec->irq, ckdev);
}
/* Clear any keys in the buffer */
static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev)
{
uint8_t old_state[ckdev->cols];
uint8_t new_state[ckdev->cols];
unsigned long duration;
int i, ret;
/*
* Keep reading until we see that the scan state does not change.
* That indicates that we are done.
*
* Assume that the EC keyscan buffer is at most 32 deep.
*/
duration = jiffies;
ret = cros_ec_keyb_get_state(ckdev, new_state);
for (i = 1; !ret && i < 32; i++) {
memcpy(old_state, new_state, sizeof(old_state));
ret = cros_ec_keyb_get_state(ckdev, new_state);
if (0 == memcmp(old_state, new_state, sizeof(old_state)))
break;
}
duration = jiffies - duration;
dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i,
jiffies_to_usecs(duration));
}
static int cros_ec_keyb_probe(struct platform_device *pdev)
{
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
struct device *dev = ec->dev;
struct cros_ec_keyb *ckdev;
struct input_dev *idev;
struct device_node *np;
int err;
np = pdev->dev.of_node;
if (!np)
return -ENODEV;
ckdev = devm_kzalloc(&pdev->dev, sizeof(*ckdev), GFP_KERNEL);
if (!ckdev)
return -ENOMEM;
err = matrix_keypad_parse_of_params(&pdev->dev, &ckdev->rows,
&ckdev->cols);
if (err)
return err;
ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL);
if (!ckdev->old_kb_state)
return -ENOMEM;
idev = devm_input_allocate_device(&pdev->dev);
if (!idev)
return -ENOMEM;
if (!ec->irq) {
dev_err(dev, "no EC IRQ specified\n");
return -EINVAL;
}
ckdev->ec = ec;
ckdev->dev = dev;
dev_set_drvdata(&pdev->dev, ckdev);
idev->name = ec->ec_name;
idev->phys = ec->phys_name;
__set_bit(EV_REP, idev->evbit);
idev->id.bustype = BUS_VIRTUAL;
idev->id.version = 1;
idev->id.product = 0;
idev->dev.parent = &pdev->dev;
idev->open = cros_ec_keyb_open;
idev->close = cros_ec_keyb_close;
ckdev->ghost_filter = of_property_read_bool(np,
"google,needs-ghost-filter");
err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols,
NULL, idev);
if (err) {
dev_err(dev, "cannot build key matrix\n");
return err;
}
ckdev->row_shift = get_count_order(ckdev->cols);
input_set_capability(idev, EV_MSC, MSC_SCAN);
input_set_drvdata(idev, ckdev);
ckdev->idev = idev;
err = input_register_device(ckdev->idev);
if (err) {
dev_err(dev, "cannot register input device\n");
return err;
}
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int cros_ec_keyb_resume(struct device *dev)
{
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
/*
* When the EC is not a wake source, then it could not have caused the
* resume, so we clear the EC's key scan buffer. If the EC was a
* wake source (e.g. the lid is open and the user might press a key to
* wake) then the key scan buffer should be preserved.
*/
if (!ckdev->ec->was_wake_device)
cros_ec_keyb_clear_keyboard(ckdev);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume);
static struct platform_driver cros_ec_keyb_driver = {
.probe = cros_ec_keyb_probe,
.driver = {
.name = "cros-ec-keyb",
.pm = &cros_ec_keyb_pm_ops,
},
};
module_platform_driver(cros_ec_keyb_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS EC keyboard driver");
MODULE_ALIAS("platform:cros-ec-keyb");

View File

@@ -0,0 +1,336 @@
/*
* DaVinci Key Scan Driver for TI platforms
*
* Copyright (C) 2009 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
*
* Initial Code: Sandeep Paulraj <s-paulraj@ti.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
*/
#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 <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <linux/platform_data/keyscan-davinci.h>
/* Key scan registers */
#define DAVINCI_KEYSCAN_KEYCTRL 0x0000
#define DAVINCI_KEYSCAN_INTENA 0x0004
#define DAVINCI_KEYSCAN_INTFLAG 0x0008
#define DAVINCI_KEYSCAN_INTCLR 0x000c
#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010
#define DAVINCI_KEYSCAN_INTERVAL 0x0014
#define DAVINCI_KEYSCAN_CONTTIME 0x0018
#define DAVINCI_KEYSCAN_CURRENTST 0x001c
#define DAVINCI_KEYSCAN_PREVSTATE 0x0020
#define DAVINCI_KEYSCAN_EMUCTRL 0x0024
#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c
/* Key Control Register (KEYCTRL) */
#define DAVINCI_KEYSCAN_KEYEN 0x00000001
#define DAVINCI_KEYSCAN_PREVMODE 0x00000002
#define DAVINCI_KEYSCAN_CHATOFF 0x00000004
#define DAVINCI_KEYSCAN_AUTODET 0x00000008
#define DAVINCI_KEYSCAN_SCANMODE 0x00000010
#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020
/* Masks for the interrupts */
#define DAVINCI_KEYSCAN_INT_CONT 0x00000008
#define DAVINCI_KEYSCAN_INT_OFF 0x00000004
#define DAVINCI_KEYSCAN_INT_ON 0x00000002
#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001
#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f
struct davinci_ks {
struct input_dev *input;
struct davinci_ks_platform_data *pdata;
int irq;
void __iomem *base;
resource_size_t pbase;
size_t base_size;
unsigned short keymap[];
};
/* Initializing the kp Module */
static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks)
{
struct device *dev = &davinci_ks->input->dev;
struct davinci_ks_platform_data *pdata = davinci_ks->pdata;
u32 matrix_ctrl;
/* Enable all interrupts */
__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
/* Clear interrupts if any */
__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
/* Setup the scan period = strobe + interval */
__raw_writel(pdata->strobe,
davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH);
__raw_writel(pdata->interval,
davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL);
__raw_writel(0x01,
davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME);
/* Define matrix type */
switch (pdata->matrix_type) {
case DAVINCI_KEYSCAN_MATRIX_4X4:
matrix_ctrl = 0;
break;
case DAVINCI_KEYSCAN_MATRIX_5X3:
matrix_ctrl = (1 << 6);
break;
default:
dev_err(dev->parent, "wrong matrix type\n");
return -EINVAL;
}
/* Enable key scan module and set matrix type */
__raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN |
matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL);
return 0;
}
static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id)
{
struct davinci_ks *davinci_ks = dev_id;
struct device *dev = &davinci_ks->input->dev;
unsigned short *keymap = davinci_ks->keymap;
int keymapsize = davinci_ks->pdata->keymapsize;
u32 prev_status, new_status, changed;
bool release;
int keycode = KEY_UNKNOWN;
int i;
/* Disable interrupt */
__raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
/* Reading previous and new status of the key scan */
prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE);
new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST);
changed = prev_status ^ new_status;
if (changed) {
/*
* It goes through all bits in 'changed' to ensure
* that no key changes are being missed
*/
for (i = 0 ; i < keymapsize; i++) {
if ((changed>>i) & 0x1) {
keycode = keymap[i];
release = (new_status >> i) & 0x1;
dev_dbg(dev->parent, "key %d %s\n", keycode,
release ? "released" : "pressed");
input_report_key(davinci_ks->input, keycode,
!release);
input_sync(davinci_ks->input);
}
}
/* Clearing interrupt */
__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
}
/* Enable interrupts */
__raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
return IRQ_HANDLED;
}
static int __init davinci_ks_probe(struct platform_device *pdev)
{
struct davinci_ks *davinci_ks;
struct input_dev *key_dev;
struct resource *res, *mem;
struct device *dev = &pdev->dev;
struct davinci_ks_platform_data *pdata = pdev->dev.platform_data;
int error, i;
if (pdata->device_enable) {
error = pdata->device_enable(dev);
if (error < 0) {
dev_dbg(dev, "device enable function failed\n");
return error;
}
}
if (!pdata->keymap) {
dev_dbg(dev, "no keymap from pdata\n");
return -EINVAL;
}
davinci_ks = kzalloc(sizeof(struct davinci_ks) +
sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL);
if (!davinci_ks) {
dev_dbg(dev, "could not allocate memory for private data\n");
return -ENOMEM;
}
memcpy(davinci_ks->keymap, pdata->keymap,
sizeof(unsigned short) * pdata->keymapsize);
key_dev = input_allocate_device();
if (!key_dev) {
dev_dbg(dev, "could not allocate input device\n");
error = -ENOMEM;
goto fail1;
}
davinci_ks->input = key_dev;
davinci_ks->irq = platform_get_irq(pdev, 0);
if (davinci_ks->irq < 0) {
dev_err(dev, "no key scan irq\n");
error = davinci_ks->irq;
goto fail2;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no mem resource\n");
error = -EINVAL;
goto fail2;
}
davinci_ks->pbase = res->start;
davinci_ks->base_size = resource_size(res);
mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size,
pdev->name);
if (!mem) {
dev_err(dev, "key scan registers at %08x are not free\n",
davinci_ks->pbase);
error = -EBUSY;
goto fail2;
}
davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size);
if (!davinci_ks->base) {
dev_err(dev, "can't ioremap MEM resource.\n");
error = -ENOMEM;
goto fail3;
}
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, key_dev->evbit);
/* Setup input device */
__set_bit(EV_KEY, key_dev->evbit);
/* Setup the platform data */
davinci_ks->pdata = pdata;
for (i = 0; i < davinci_ks->pdata->keymapsize; i++)
__set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit);
key_dev->name = "davinci_keyscan";
key_dev->phys = "davinci_keyscan/input0";
key_dev->dev.parent = &pdev->dev;
key_dev->id.bustype = BUS_HOST;
key_dev->id.vendor = 0x0001;
key_dev->id.product = 0x0001;
key_dev->id.version = 0x0001;
key_dev->keycode = davinci_ks->keymap;
key_dev->keycodesize = sizeof(davinci_ks->keymap[0]);
key_dev->keycodemax = davinci_ks->pdata->keymapsize;
error = input_register_device(davinci_ks->input);
if (error < 0) {
dev_err(dev, "unable to register davinci key scan device\n");
goto fail4;
}
error = request_irq(davinci_ks->irq, davinci_ks_interrupt,
0, pdev->name, davinci_ks);
if (error < 0) {
dev_err(dev, "unable to register davinci key scan interrupt\n");
goto fail5;
}
error = davinci_ks_initialize(davinci_ks);
if (error < 0) {
dev_err(dev, "unable to initialize davinci key scan device\n");
goto fail6;
}
platform_set_drvdata(pdev, davinci_ks);
return 0;
fail6:
free_irq(davinci_ks->irq, davinci_ks);
fail5:
input_unregister_device(davinci_ks->input);
key_dev = NULL;
fail4:
iounmap(davinci_ks->base);
fail3:
release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
fail2:
input_free_device(key_dev);
fail1:
kfree(davinci_ks);
return error;
}
static int davinci_ks_remove(struct platform_device *pdev)
{
struct davinci_ks *davinci_ks = platform_get_drvdata(pdev);
free_irq(davinci_ks->irq, davinci_ks);
input_unregister_device(davinci_ks->input);
iounmap(davinci_ks->base);
release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
platform_set_drvdata(pdev, NULL);
kfree(davinci_ks);
return 0;
}
static struct platform_driver davinci_ks_driver = {
.driver = {
.name = "davinci_keyscan",
.owner = THIS_MODULE,
},
.remove = davinci_ks_remove,
};
module_platform_driver_probe(davinci_ks_driver, davinci_ks_probe);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,390 @@
/*
* Driver for the Cirrus EP93xx matrix keypad controller.
*
* Copyright (c) 2008 H Hartley Sweeten <hsweeten@visionengravers.com>
*
* Based on the pxa27x matrix keypad controller by Rodolfo Giometti.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* NOTE:
*
* The 3-key reset is triggered by pressing the 3 keys in
* Row 0, Columns 2, 4, and 7 at the same time. This action can
* be disabled by setting the EP93XX_KEYPAD_DISABLE_3_KEY flag.
*
* Normal operation for the matrix does not autorepeat the key press.
* This action can be enabled by setting the EP93XX_KEYPAD_AUTOREPEAT
* flag.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <mach/hardware.h>
#include <linux/platform_data/keypad-ep93xx.h>
/*
* Keypad Interface Register offsets
*/
#define KEY_INIT 0x00 /* Key Scan Initialization register */
#define KEY_DIAG 0x04 /* Key Scan Diagnostic register */
#define KEY_REG 0x08 /* Key Value Capture register */
/* Key Scan Initialization Register bit defines */
#define KEY_INIT_DBNC_MASK (0x00ff0000)
#define KEY_INIT_DBNC_SHIFT (16)
#define KEY_INIT_DIS3KY (1<<15)
#define KEY_INIT_DIAG (1<<14)
#define KEY_INIT_BACK (1<<13)
#define KEY_INIT_T2 (1<<12)
#define KEY_INIT_PRSCL_MASK (0x000003ff)
#define KEY_INIT_PRSCL_SHIFT (0)
/* Key Scan Diagnostic Register bit defines */
#define KEY_DIAG_MASK (0x0000003f)
#define KEY_DIAG_SHIFT (0)
/* Key Value Capture Register bit defines */
#define KEY_REG_K (1<<15)
#define KEY_REG_INT (1<<14)
#define KEY_REG_2KEYS (1<<13)
#define KEY_REG_1KEY (1<<12)
#define KEY_REG_KEY2_MASK (0x00000fc0)
#define KEY_REG_KEY2_SHIFT (6)
#define KEY_REG_KEY1_MASK (0x0000003f)
#define KEY_REG_KEY1_SHIFT (0)
#define EP93XX_MATRIX_SIZE (EP93XX_MATRIX_ROWS * EP93XX_MATRIX_COLS)
struct ep93xx_keypad {
struct ep93xx_keypad_platform_data *pdata;
struct input_dev *input_dev;
struct clk *clk;
void __iomem *mmio_base;
unsigned short keycodes[EP93XX_MATRIX_SIZE];
int key1;
int key2;
int irq;
bool enabled;
};
static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id)
{
struct ep93xx_keypad *keypad = dev_id;
struct input_dev *input_dev = keypad->input_dev;
unsigned int status;
int keycode, key1, key2;
status = __raw_readl(keypad->mmio_base + KEY_REG);
keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT;
key1 = keypad->keycodes[keycode];
keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT;
key2 = keypad->keycodes[keycode];
if (status & KEY_REG_2KEYS) {
if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1)
input_report_key(input_dev, keypad->key1, 0);
if (keypad->key2 && key1 != keypad->key2 && key2 != keypad->key2)
input_report_key(input_dev, keypad->key2, 0);
input_report_key(input_dev, key1, 1);
input_report_key(input_dev, key2, 1);
keypad->key1 = key1;
keypad->key2 = key2;
} else if (status & KEY_REG_1KEY) {
if (keypad->key1 && key1 != keypad->key1)
input_report_key(input_dev, keypad->key1, 0);
if (keypad->key2 && key1 != keypad->key2)
input_report_key(input_dev, keypad->key2, 0);
input_report_key(input_dev, key1, 1);
keypad->key1 = key1;
keypad->key2 = 0;
} else {
input_report_key(input_dev, keypad->key1, 0);
input_report_key(input_dev, keypad->key2, 0);
keypad->key1 = keypad->key2 = 0;
}
input_sync(input_dev);
return IRQ_HANDLED;
}
static void ep93xx_keypad_config(struct ep93xx_keypad *keypad)
{
struct ep93xx_keypad_platform_data *pdata = keypad->pdata;
unsigned int val = 0;
if (pdata->flags & EP93XX_KEYPAD_KDIV)
clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV4);
else
clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV16);
if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY)
val |= KEY_INIT_DIS3KY;
if (pdata->flags & EP93XX_KEYPAD_DIAG_MODE)
val |= KEY_INIT_DIAG;
if (pdata->flags & EP93XX_KEYPAD_BACK_DRIVE)
val |= KEY_INIT_BACK;
if (pdata->flags & EP93XX_KEYPAD_TEST_MODE)
val |= KEY_INIT_T2;
val |= ((pdata->debounce << KEY_INIT_DBNC_SHIFT) & KEY_INIT_DBNC_MASK);
val |= ((pdata->prescale << KEY_INIT_PRSCL_SHIFT) & KEY_INIT_PRSCL_MASK);
__raw_writel(val, keypad->mmio_base + KEY_INIT);
}
static int ep93xx_keypad_open(struct input_dev *pdev)
{
struct ep93xx_keypad *keypad = input_get_drvdata(pdev);
if (!keypad->enabled) {
ep93xx_keypad_config(keypad);
clk_enable(keypad->clk);
keypad->enabled = true;
}
return 0;
}
static void ep93xx_keypad_close(struct input_dev *pdev)
{
struct ep93xx_keypad *keypad = input_get_drvdata(pdev);
if (keypad->enabled) {
clk_disable(keypad->clk);
keypad->enabled = false;
}
}
#ifdef CONFIG_PM_SLEEP
static int ep93xx_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
mutex_lock(&input_dev->mutex);
if (keypad->enabled) {
clk_disable(keypad->clk);
keypad->enabled = false;
}
mutex_unlock(&input_dev->mutex);
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(keypad->irq);
return 0;
}
static int ep93xx_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(keypad->irq);
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
if (!keypad->enabled) {
ep93xx_keypad_config(keypad);
clk_enable(keypad->clk);
keypad->enabled = true;
}
}
mutex_unlock(&input_dev->mutex);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops,
ep93xx_keypad_suspend, ep93xx_keypad_resume);
static int ep93xx_keypad_probe(struct platform_device *pdev)
{
struct ep93xx_keypad *keypad;
const struct matrix_keymap_data *keymap_data;
struct input_dev *input_dev;
struct resource *res;
int err;
keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL);
if (!keypad)
return -ENOMEM;
keypad->pdata = pdev->dev.platform_data;
if (!keypad->pdata) {
err = -EINVAL;
goto failed_free;
}
keymap_data = keypad->pdata->keymap_data;
if (!keymap_data) {
err = -EINVAL;
goto failed_free;
}
keypad->irq = platform_get_irq(pdev, 0);
if (!keypad->irq) {
err = -ENXIO;
goto failed_free;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
err = -ENXIO;
goto failed_free;
}
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (!res) {
err = -EBUSY;
goto failed_free;
}
keypad->mmio_base = ioremap(res->start, resource_size(res));
if (keypad->mmio_base == NULL) {
err = -ENXIO;
goto failed_free_mem;
}
err = ep93xx_keypad_acquire_gpio(pdev);
if (err)
goto failed_free_io;
keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
err = PTR_ERR(keypad->clk);
goto failed_free_gpio;
}
input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
goto failed_put_clk;
}
keypad->input_dev = input_dev;
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = ep93xx_keypad_open;
input_dev->close = ep93xx_keypad_close;
input_dev->dev.parent = &pdev->dev;
err = matrix_keypad_build_keymap(keymap_data, NULL,
EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS,
keypad->keycodes, input_dev);
if (err)
goto failed_free_dev;
if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT)
__set_bit(EV_REP, input_dev->evbit);
input_set_drvdata(input_dev, keypad);
err = request_irq(keypad->irq, ep93xx_keypad_irq_handler,
0, pdev->name, keypad);
if (err)
goto failed_free_dev;
err = input_register_device(input_dev);
if (err)
goto failed_free_irq;
platform_set_drvdata(pdev, keypad);
device_init_wakeup(&pdev->dev, 1);
return 0;
failed_free_irq:
free_irq(keypad->irq, pdev);
platform_set_drvdata(pdev, NULL);
failed_free_dev:
input_free_device(input_dev);
failed_put_clk:
clk_put(keypad->clk);
failed_free_gpio:
ep93xx_keypad_release_gpio(pdev);
failed_free_io:
iounmap(keypad->mmio_base);
failed_free_mem:
release_mem_region(res->start, resource_size(res));
failed_free:
kfree(keypad);
return err;
}
static int ep93xx_keypad_remove(struct platform_device *pdev)
{
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
struct resource *res;
free_irq(keypad->irq, pdev);
platform_set_drvdata(pdev, NULL);
if (keypad->enabled)
clk_disable(keypad->clk);
clk_put(keypad->clk);
input_unregister_device(keypad->input_dev);
ep93xx_keypad_release_gpio(pdev);
iounmap(keypad->mmio_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
kfree(keypad);
return 0;
}
static struct platform_driver ep93xx_keypad_driver = {
.driver = {
.name = "ep93xx-keypad",
.owner = THIS_MODULE,
.pm = &ep93xx_keypad_pm_ops,
},
.probe = ep93xx_keypad_probe,
.remove = ep93xx_keypad_remove,
};
module_platform_driver(ep93xx_keypad_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_DESCRIPTION("EP93xx Matrix Keypad Controller");
MODULE_ALIAS("platform:ep93xx-keypad");

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2007 Google, Inc.
* Copyright (C) 2012 Intel, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#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/platform_device.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/io.h>
enum {
REG_READ = 0x00,
REG_SET_PAGE = 0x00,
REG_LEN = 0x04,
REG_DATA = 0x08,
PAGE_NAME = 0x00000,
PAGE_EVBITS = 0x10000,
PAGE_ABSDATA = 0x20000 | EV_ABS,
};
struct event_dev {
struct input_dev *input;
int irq;
void __iomem *addr;
char name[0];
};
static irqreturn_t events_interrupt(int irq, void *dev_id)
{
struct event_dev *edev = dev_id;
unsigned type, code, value;
type = __raw_readl(edev->addr + REG_READ);
code = __raw_readl(edev->addr + REG_READ);
value = __raw_readl(edev->addr + REG_READ);
input_event(edev->input, type, code, value);
input_sync(edev->input);
return IRQ_HANDLED;
}
static void events_import_bits(struct event_dev *edev,
unsigned long bits[], unsigned type, size_t count)
{
void __iomem *addr = edev->addr;
int i, j;
size_t size;
uint8_t val;
__raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
size = __raw_readl(addr + REG_LEN) * 8;
if (size < count)
count = size;
addr += REG_DATA;
for (i = 0; i < count; i += 8) {
val = __raw_readb(addr++);
for (j = 0; j < 8; j++)
if (val & 1 << j)
set_bit(i + j, bits);
}
}
static void events_import_abs_params(struct event_dev *edev)
{
struct input_dev *input_dev = edev->input;
void __iomem *addr = edev->addr;
u32 val[4];
int count;
int i, j;
__raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
count = __raw_readl(addr + REG_LEN) / sizeof(val);
if (count > ABS_MAX)
count = ABS_MAX;
for (i = 0; i < count; i++) {
if (!test_bit(i, input_dev->absbit))
continue;
for (j = 0; j < ARRAY_SIZE(val); j++) {
int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32);
val[j] = __raw_readl(edev->addr + REG_DATA + offset);
}
input_set_abs_params(input_dev, i,
val[0], val[1], val[2], val[3]);
}
}
static int events_probe(struct platform_device *pdev)
{
struct input_dev *input_dev;
struct event_dev *edev;
struct resource *res;
unsigned keymapnamelen;
void __iomem *addr;
int irq;
int i;
int error;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
addr = devm_ioremap(&pdev->dev, res->start, 4096);
if (!addr)
return -ENOMEM;
__raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
keymapnamelen = __raw_readl(addr + REG_LEN);
edev = devm_kzalloc(&pdev->dev,
sizeof(struct event_dev) + keymapnamelen + 1,
GFP_KERNEL);
if (!edev)
return -ENOMEM;
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev)
return -ENOMEM;
edev->input = input_dev;
edev->addr = addr;
edev->irq = irq;
for (i = 0; i < keymapnamelen; i++)
edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
pr_debug("events_probe() keymap=%s\n", edev->name);
input_dev->name = edev->name;
input_dev->id.bustype = BUS_HOST;
events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
events_import_abs_params(edev);
error = devm_request_irq(&pdev->dev, edev->irq, events_interrupt, 0,
"goldfish-events-keypad", edev);
if (error)
return error;
error = input_register_device(input_dev);
if (error)
return error;
return 0;
}
static struct platform_driver events_driver = {
.probe = events_probe,
.driver = {
.owner = THIS_MODULE,
.name = "goldfish_events",
},
};
module_platform_driver(events_driver);
MODULE_AUTHOR("Brian Swetland");
MODULE_DESCRIPTION("Goldfish Event Device");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,885 @@
/*
* Driver for keys on GPIO lines capable of generating interrupts.
*
* Copyright 2005 Phil Blundell
* Copyright 2010, 2011 David Jander <david@protonic.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>
struct gpio_button_data {
const struct gpio_keys_button *button;
struct input_dev *input;
struct timer_list timer;
struct work_struct work;
unsigned int timer_debounce; /* in msecs */
unsigned int irq;
spinlock_t lock;
bool disabled;
bool key_pressed;
};
struct gpio_keys_drvdata {
const struct gpio_keys_platform_data *pdata;
struct input_dev *input;
struct mutex disable_lock;
struct gpio_button_data data[0];
};
/*
* SYSFS interface for enabling/disabling keys and switches:
*
* There are 4 attributes under /sys/devices/platform/gpio-keys/
* keys [ro] - bitmap of keys (EV_KEY) which can be
* disabled
* switches [ro] - bitmap of switches (EV_SW) which can be
* disabled
* disabled_keys [rw] - bitmap of keys currently disabled
* disabled_switches [rw] - bitmap of switches currently disabled
*
* Userland can change these values and hence disable event generation
* for each key (or switch). Disabling a key means its interrupt line
* is disabled.
*
* For example, if we have following switches set up as gpio-keys:
* SW_DOCK = 5
* SW_CAMERA_LENS_COVER = 9
* SW_KEYPAD_SLIDE = 10
* SW_FRONT_PROXIMITY = 11
* This is read from switches:
* 11-9,5
* Next we want to disable proximity (11) and dock (5), we write:
* 11,5
* to file disabled_switches. Now proximity and dock IRQs are disabled.
* This can be verified by reading the file disabled_switches:
* 11,5
* If we now want to enable proximity (11) switch we write:
* 5
* to disabled_switches.
*
* We can disable only those keys which don't allow sharing the irq.
*/
/**
* get_n_events_by_type() - returns maximum number of events per @type
* @type: type of button (%EV_KEY, %EV_SW)
*
* Return value of this function can be used to allocate bitmap
* large enough to hold all bits for given type.
*/
static inline int get_n_events_by_type(int type)
{
BUG_ON(type != EV_SW && type != EV_KEY);
return (type == EV_KEY) ? KEY_CNT : SW_CNT;
}
/**
* gpio_keys_disable_button() - disables given GPIO button
* @bdata: button data for button to be disabled
*
* Disables button pointed by @bdata. This is done by masking
* IRQ line. After this function is called, button won't generate
* input events anymore. Note that one can only disable buttons
* that don't share IRQs.
*
* Make sure that @bdata->disable_lock is locked when entering
* this function to avoid races when concurrent threads are
* disabling buttons at the same time.
*/
static void gpio_keys_disable_button(struct gpio_button_data *bdata)
{
if (!bdata->disabled) {
/*
* Disable IRQ and possible debouncing timer.
*/
disable_irq(bdata->irq);
if (bdata->timer_debounce)
del_timer_sync(&bdata->timer);
bdata->disabled = true;
}
}
/**
* gpio_keys_enable_button() - enables given GPIO button
* @bdata: button data for button to be disabled
*
* Enables given button pointed by @bdata.
*
* Make sure that @bdata->disable_lock is locked when entering
* this function to avoid races with concurrent threads trying
* to enable the same button at the same time.
*/
static void gpio_keys_enable_button(struct gpio_button_data *bdata)
{
if (bdata->disabled) {
enable_irq(bdata->irq);
bdata->disabled = false;
}
}
/**
* gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons
* @ddata: pointer to drvdata
* @buf: buffer where stringified bitmap is written
* @type: button type (%EV_KEY, %EV_SW)
* @only_disabled: does caller want only those buttons that are
* currently disabled or all buttons that can be
* disabled
*
* This function writes buttons that can be disabled to @buf. If
* @only_disabled is true, then @buf contains only those buttons
* that are currently disabled. Returns 0 on success or negative
* errno on failure.
*/
static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
char *buf, unsigned int type,
bool only_disabled)
{
int n_events = get_n_events_by_type(type);
unsigned long *bits;
ssize_t ret;
int i;
bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL);
if (!bits)
return -ENOMEM;
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->type != type)
continue;
if (only_disabled && !bdata->disabled)
continue;
__set_bit(bdata->button->code, bits);
}
ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events);
buf[ret++] = '\n';
buf[ret] = '\0';
kfree(bits);
return ret;
}
/**
* gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap
* @ddata: pointer to drvdata
* @buf: buffer from userspace that contains stringified bitmap
* @type: button type (%EV_KEY, %EV_SW)
*
* This function parses stringified bitmap from @buf and disables/enables
* GPIO buttons accordingly. Returns 0 on success and negative error
* on failure.
*/
static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
const char *buf, unsigned int type)
{
int n_events = get_n_events_by_type(type);
unsigned long *bits;
ssize_t error;
int i;
bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL);
if (!bits)
return -ENOMEM;
error = bitmap_parselist(buf, bits, n_events);
if (error)
goto out;
/* First validate */
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->type != type)
continue;
if (test_bit(bdata->button->code, bits) &&
!bdata->button->can_disable) {
error = -EINVAL;
goto out;
}
}
mutex_lock(&ddata->disable_lock);
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->type != type)
continue;
if (test_bit(bdata->button->code, bits))
gpio_keys_disable_button(bdata);
else
gpio_keys_enable_button(bdata);
}
mutex_unlock(&ddata->disable_lock);
out:
kfree(bits);
return error;
}
#define ATTR_SHOW_FN(name, type, only_disabled) \
static ssize_t gpio_keys_show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct platform_device *pdev = to_platform_device(dev); \
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \
\
return gpio_keys_attr_show_helper(ddata, buf, \
type, only_disabled); \
}
ATTR_SHOW_FN(keys, EV_KEY, false);
ATTR_SHOW_FN(switches, EV_SW, false);
ATTR_SHOW_FN(disabled_keys, EV_KEY, true);
ATTR_SHOW_FN(disabled_switches, EV_SW, true);
/*
* ATTRIBUTES:
*
* /sys/devices/platform/gpio-keys/keys [ro]
* /sys/devices/platform/gpio-keys/switches [ro]
*/
static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL);
static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL);
#define ATTR_STORE_FN(name, type) \
static ssize_t gpio_keys_store_##name(struct device *dev, \
struct device_attribute *attr, \
const char *buf, \
size_t count) \
{ \
struct platform_device *pdev = to_platform_device(dev); \
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \
ssize_t error; \
\
error = gpio_keys_attr_store_helper(ddata, buf, type); \
if (error) \
return error; \
\
return count; \
}
ATTR_STORE_FN(disabled_keys, EV_KEY);
ATTR_STORE_FN(disabled_switches, EV_SW);
/*
* ATTRIBUTES:
*
* /sys/devices/platform/gpio-keys/disabled_keys [rw]
* /sys/devices/platform/gpio-keys/disables_switches [rw]
*/
static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO,
gpio_keys_show_disabled_keys,
gpio_keys_store_disabled_keys);
static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO,
gpio_keys_show_disabled_switches,
gpio_keys_store_disabled_switches);
static struct attribute *gpio_keys_attrs[] = {
&dev_attr_keys.attr,
&dev_attr_switches.attr,
&dev_attr_disabled_keys.attr,
&dev_attr_disabled_switches.attr,
NULL,
};
static struct attribute_group gpio_keys_attr_group = {
.attrs = gpio_keys_attrs,
};
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
} else {
input_event(input, type, button->code, !!state);
}
input_sync(input);
}
static void gpio_keys_gpio_work_func(struct work_struct *work)
{
struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work);
gpio_keys_gpio_report_event(bdata);
if (bdata->button->wakeup)
pm_relax(bdata->input->dev.parent);
}
static void gpio_keys_gpio_timer(unsigned long _data)
{
struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
schedule_work(&bdata->work);
}
static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
BUG_ON(irq != bdata->irq);
if (bdata->button->wakeup)
pm_stay_awake(bdata->input->dev.parent);
if (bdata->timer_debounce)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(bdata->timer_debounce));
else
schedule_work(&bdata->work);
return IRQ_HANDLED;
}
static void gpio_keys_irq_timer(unsigned long _data)
{
struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
struct input_dev *input = bdata->input;
unsigned long flags;
spin_lock_irqsave(&bdata->lock, flags);
if (bdata->key_pressed) {
input_event(input, EV_KEY, bdata->button->code, 0);
input_sync(input);
bdata->key_pressed = false;
}
spin_unlock_irqrestore(&bdata->lock, flags);
}
static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned long flags;
BUG_ON(irq != bdata->irq);
spin_lock_irqsave(&bdata->lock, flags);
if (!bdata->key_pressed) {
if (bdata->button->wakeup)
pm_wakeup_event(bdata->input->dev.parent, 0);
input_event(input, EV_KEY, button->code, 1);
input_sync(input);
if (!bdata->timer_debounce) {
input_event(input, EV_KEY, button->code, 0);
input_sync(input);
goto out;
}
bdata->key_pressed = true;
}
if (bdata->timer_debounce)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(bdata->timer_debounce));
out:
spin_unlock_irqrestore(&bdata->lock, flags);
return IRQ_HANDLED;
}
static int gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input,
struct gpio_button_data *bdata,
const struct gpio_keys_button *button)
{
const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
irq_handler_t isr;
unsigned long irqflags;
int irq, error;
bdata->input = input;
bdata->button = button;
spin_lock_init(&bdata->lock);
if (gpio_is_valid(button->gpio)) {
error = gpio_request_one(button->gpio, GPIOF_IN, desc);
if (error < 0) {
dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->gpio, error);
return error;
}
if (button->debounce_interval) {
error = gpio_set_debounce(button->gpio,
button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
bdata->timer_debounce =
button->debounce_interval;
}
irq = gpio_to_irq(button->gpio);
if (irq < 0) {
error = irq;
dev_err(dev,
"Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error);
goto fail;
}
bdata->irq = irq;
INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
setup_timer(&bdata->timer,
gpio_keys_gpio_timer, (unsigned long)bdata);
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
} else {
if (!button->irq) {
dev_err(dev, "No IRQ specified\n");
return -EINVAL;
}
bdata->irq = button->irq;
if (button->type && button->type != EV_KEY) {
dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
return -EINVAL;
}
bdata->timer_debounce = button->debounce_interval;
setup_timer(&bdata->timer,
gpio_keys_irq_timer, (unsigned long)bdata);
isr = gpio_keys_irq_isr;
irqflags = 0;
}
input_set_capability(input, button->type ?: EV_KEY, button->code);
/*
* If platform has specified that the button can be disabled,
* we don't want it to share the interrupt line.
*/
if (!button->can_disable)
irqflags |= IRQF_SHARED;
error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata);
if (error < 0) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
bdata->irq, error);
goto fail;
}
return 0;
fail:
if (gpio_is_valid(button->gpio))
gpio_free(button->gpio);
return error;
}
static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata)
{
struct input_dev *input = ddata->input;
int i;
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (gpio_is_valid(bdata->button->gpio))
gpio_keys_gpio_report_event(bdata);
}
input_sync(input);
}
static int gpio_keys_open(struct input_dev *input)
{
struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
const struct gpio_keys_platform_data *pdata = ddata->pdata;
int error;
if (pdata->enable) {
error = pdata->enable(input->dev.parent);
if (error)
return error;
}
/* Report current state of buttons that are connected to GPIOs */
gpio_keys_report_state(ddata);
return 0;
}
static void gpio_keys_close(struct input_dev *input)
{
struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
const struct gpio_keys_platform_data *pdata = ddata->pdata;
if (pdata->disable)
pdata->disable(input->dev.parent);
}
/*
* Handlers for alternative sources of platform_data
*/
#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
struct device_node *node, *pp;
struct gpio_keys_platform_data *pdata;
struct gpio_keys_button *button;
int error;
int nbuttons;
int i;
node = dev->of_node;
if (!node) {
error = -ENODEV;
goto err_out;
}
nbuttons = of_get_child_count(node);
if (nbuttons == 0) {
error = -ENODEV;
goto err_out;
}
pdata = kzalloc(sizeof(*pdata) + nbuttons * (sizeof *button),
GFP_KERNEL);
if (!pdata) {
error = -ENOMEM;
goto err_out;
}
pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
pdata->nbuttons = nbuttons;
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
i = 0;
for_each_child_of_node(node, pp) {
int gpio;
enum of_gpio_flags flags;
if (!of_find_property(pp, "gpios", NULL)) {
pdata->nbuttons--;
dev_warn(dev, "Found button without gpios\n");
continue;
}
gpio = of_get_gpio_flags(pp, 0, &flags);
if (gpio < 0) {
error = gpio;
if (error != -EPROBE_DEFER)
dev_err(dev,
"Failed to get gpio flags, error: %d\n",
error);
goto err_free_pdata;
}
button = &pdata->buttons[i++];
button->gpio = gpio;
button->active_low = flags & OF_GPIO_ACTIVE_LOW;
if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n",
button->gpio);
error = -EINVAL;
goto err_free_pdata;
}
button->desc = of_get_property(pp, "label", NULL);
if (of_property_read_u32(pp, "linux,input-type", &button->type))
button->type = EV_KEY;
button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
if (of_property_read_u32(pp, "debounce-interval",
&button->debounce_interval))
button->debounce_interval = 5;
}
if (pdata->nbuttons == 0) {
error = -EINVAL;
goto err_free_pdata;
}
return pdata;
err_free_pdata:
kfree(pdata);
err_out:
return ERR_PTR(error);
}
static struct of_device_id gpio_keys_of_match[] = {
{ .compatible = "gpio-keys", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
#else
static inline struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
return ERR_PTR(-ENODEV);
}
#endif
static void gpio_remove_key(struct gpio_button_data *bdata)
{
free_irq(bdata->irq, bdata);
if (bdata->timer_debounce)
del_timer_sync(&bdata->timer);
cancel_work_sync(&bdata->work);
if (gpio_is_valid(bdata->button->gpio))
gpio_free(bdata->button->gpio);
}
static int gpio_keys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct gpio_keys_drvdata *ddata;
struct input_dev *input;
int i, error;
int wakeup = 0;
if (!pdata) {
pdata = gpio_keys_get_devtree_pdata(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
input = input_allocate_device();
if (!ddata || !input) {
dev_err(dev, "failed to allocate state\n");
error = -ENOMEM;
goto fail1;
}
ddata->pdata = pdata;
ddata->input = input;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev, ddata);
input_set_drvdata(input, ddata);
input->name = pdata->name ? : pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->open = gpio_keys_open;
input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
const struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
error = gpio_keys_setup_key(pdev, input, bdata, button);
if (error)
goto fail2;
if (button->wakeup)
wakeup = 1;
}
error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
if (error) {
dev_err(dev, "Unable to export keys/switches, error: %d\n",
error);
goto fail2;
}
error = input_register_device(input);
if (error) {
dev_err(dev, "Unable to register input device, error: %d\n",
error);
goto fail3;
}
device_init_wakeup(&pdev->dev, wakeup);
return 0;
fail3:
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
fail2:
while (--i >= 0)
gpio_remove_key(&ddata->data[i]);
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
/* If we have no platform data, we allocated pdata dynamically. */
if (!dev_get_platdata(&pdev->dev))
kfree(pdata);
return error;
}
static int gpio_keys_remove(struct platform_device *pdev)
{
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
struct input_dev *input = ddata->input;
int i;
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
device_init_wakeup(&pdev->dev, 0);
for (i = 0; i < ddata->pdata->nbuttons; i++)
gpio_remove_key(&ddata->data[i]);
input_unregister_device(input);
/* If we have no platform data, we allocated pdata dynamically. */
if (!dev_get_platdata(&pdev->dev))
kfree(ddata->pdata);
kfree(ddata);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int gpio_keys_suspend(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
int i;
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->wakeup)
enable_irq_wake(bdata->irq);
}
} else {
mutex_lock(&input->mutex);
if (input->users)
gpio_keys_close(input);
mutex_unlock(&input->mutex);
}
return 0;
}
static int gpio_keys_resume(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
int error = 0;
int i;
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->wakeup)
disable_irq_wake(bdata->irq);
}
} else {
mutex_lock(&input->mutex);
if (input->users)
error = gpio_keys_open(input);
mutex_unlock(&input->mutex);
}
if (error)
return error;
gpio_keys_report_state(ddata);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = gpio_keys_remove,
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
.pm = &gpio_keys_pm_ops,
.of_match_table = of_match_ptr(gpio_keys_of_match),
}
};
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
static void __exit gpio_keys_exit(void)
{
platform_driver_unregister(&gpio_keys_device_driver);
}
late_initcall(gpio_keys_init);
module_exit(gpio_keys_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
MODULE_DESCRIPTION("Keyboard driver for GPIOs");
MODULE_ALIAS("platform:gpio-keys");

View File

@@ -0,0 +1,377 @@
/*
* Driver for buttons on GPIO lines not capable of generating interrupts
*
* Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com>
*
* This file was based on: /drivers/input/misc/cobalt_btns.c
* Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
*
* also was based on: /drivers/input/keyboard/gpio_keys.c
* Copyright 2005 Phil Blundell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#define DRV_NAME "gpio-keys-polled"
struct gpio_keys_button_data {
int last_state;
int count;
int threshold;
int can_sleep;
};
struct gpio_keys_polled_dev {
struct input_polled_dev *poll_dev;
struct device *dev;
const struct gpio_keys_platform_data *pdata;
struct gpio_keys_button_data data[0];
};
static void gpio_keys_polled_check_state(struct input_dev *input,
struct gpio_keys_button *button,
struct gpio_keys_button_data *bdata)
{
int state;
if (bdata->can_sleep)
state = !!gpio_get_value_cansleep(button->gpio);
else
state = !!gpio_get_value(button->gpio);
if (state != bdata->last_state) {
unsigned int type = button->type ?: EV_KEY;
input_event(input, type, button->code,
!!(state ^ button->active_low));
input_sync(input);
bdata->count = 0;
bdata->last_state = state;
}
}
static void gpio_keys_polled_poll(struct input_polled_dev *dev)
{
struct gpio_keys_polled_dev *bdev = dev->private;
const struct gpio_keys_platform_data *pdata = bdev->pdata;
struct input_dev *input = dev->input;
int i;
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button_data *bdata = &bdev->data[i];
if (bdata->count < bdata->threshold)
bdata->count++;
else
gpio_keys_polled_check_state(input, &pdata->buttons[i],
bdata);
}
}
static void gpio_keys_polled_open(struct input_polled_dev *dev)
{
struct gpio_keys_polled_dev *bdev = dev->private;
const struct gpio_keys_platform_data *pdata = bdev->pdata;
if (pdata->enable)
pdata->enable(bdev->dev);
}
static void gpio_keys_polled_close(struct input_polled_dev *dev)
{
struct gpio_keys_polled_dev *bdev = dev->private;
const struct gpio_keys_platform_data *pdata = bdev->pdata;
if (pdata->disable)
pdata->disable(bdev->dev);
}
#ifdef CONFIG_OF
static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device *dev)
{
struct device_node *node, *pp;
struct gpio_keys_platform_data *pdata;
struct gpio_keys_button *button;
int error;
int nbuttons;
int i;
node = dev->of_node;
if (!node)
return NULL;
nbuttons = of_get_child_count(node);
if (nbuttons == 0)
return NULL;
pdata = kzalloc(sizeof(*pdata) + nbuttons * (sizeof *button),
GFP_KERNEL);
if (!pdata) {
error = -ENOMEM;
goto err_out;
}
pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
pdata->nbuttons = nbuttons;
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
of_property_read_u32(node, "poll-interval", &pdata->poll_interval);
i = 0;
for_each_child_of_node(node, pp) {
int gpio;
enum of_gpio_flags flags;
if (!of_find_property(pp, "gpios", NULL)) {
pdata->nbuttons--;
dev_warn(dev, "Found button without gpios\n");
continue;
}
gpio = of_get_gpio_flags(pp, 0, &flags);
if (gpio < 0) {
error = gpio;
if (error != -EPROBE_DEFER)
dev_err(dev,
"Failed to get gpio flags, error: %d\n",
error);
goto err_free_pdata;
}
button = &pdata->buttons[i++];
button->gpio = gpio;
button->active_low = flags & OF_GPIO_ACTIVE_LOW;
if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n",
button->gpio);
error = -EINVAL;
goto err_free_pdata;
}
button->desc = of_get_property(pp, "label", NULL);
if (of_property_read_u32(pp, "linux,input-type", &button->type))
button->type = EV_KEY;
button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
if (of_property_read_u32(pp, "debounce-interval",
&button->debounce_interval))
button->debounce_interval = 5;
}
if (pdata->nbuttons == 0) {
error = -EINVAL;
goto err_free_pdata;
}
return pdata;
err_free_pdata:
kfree(pdata);
err_out:
return ERR_PTR(error);
}
static struct of_device_id gpio_keys_polled_of_match[] = {
{ .compatible = "gpio-keys-polled", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
#else
static inline struct gpio_keys_platform_data *
gpio_keys_polled_get_devtree_pdata(struct device *dev)
{
return NULL;
}
#endif
static int gpio_keys_polled_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct gpio_keys_polled_dev *bdev;
struct input_polled_dev *poll_dev;
struct input_dev *input;
int error;
int i;
if (!pdata) {
pdata = gpio_keys_polled_get_devtree_pdata(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
if (!pdata) {
dev_err(dev, "missing platform data\n");
return -EINVAL;
}
}
if (!pdata->poll_interval) {
dev_err(dev, "missing poll_interval value\n");
error = -EINVAL;
goto err_free_pdata;
}
bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) +
pdata->nbuttons * sizeof(struct gpio_keys_button_data),
GFP_KERNEL);
if (!bdev) {
dev_err(dev, "no memory for private data\n");
error = -ENOMEM;
goto err_free_pdata;
}
poll_dev = input_allocate_polled_device();
if (!poll_dev) {
dev_err(dev, "no memory for polled device\n");
error = -ENOMEM;
goto err_free_bdev;
}
poll_dev->private = bdev;
poll_dev->poll = gpio_keys_polled_poll;
poll_dev->poll_interval = pdata->poll_interval;
poll_dev->open = gpio_keys_polled_open;
poll_dev->close = gpio_keys_polled_close;
input = poll_dev->input;
input->name = pdev->name;
input->phys = DRV_NAME"/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
__set_bit(EV_KEY, input->evbit);
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_keys_button_data *bdata = &bdev->data[i];
unsigned int gpio = button->gpio;
unsigned int type = button->type ?: EV_KEY;
if (button->wakeup) {
dev_err(dev, DRV_NAME " does not support wakeup\n");
error = -EINVAL;
goto err_free_gpio;
}
error = gpio_request_one(gpio, GPIOF_IN,
button->desc ?: DRV_NAME);
if (error) {
dev_err(dev, "unable to claim gpio %u, err=%d\n",
gpio, error);
goto err_free_gpio;
}
bdata->can_sleep = gpio_cansleep(gpio);
bdata->last_state = -1;
bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
pdata->poll_interval);
input_set_capability(input, type, button->code);
}
bdev->poll_dev = poll_dev;
bdev->dev = dev;
bdev->pdata = pdata;
platform_set_drvdata(pdev, bdev);
error = input_register_polled_device(poll_dev);
if (error) {
dev_err(dev, "unable to register polled device, err=%d\n",
error);
goto err_free_gpio;
}
/* report initial state of the buttons */
for (i = 0; i < pdata->nbuttons; i++)
gpio_keys_polled_check_state(input, &pdata->buttons[i],
&bdev->data[i]);
return 0;
err_free_gpio:
while (--i >= 0)
gpio_free(pdata->buttons[i].gpio);
input_free_polled_device(poll_dev);
err_free_bdev:
kfree(bdev);
platform_set_drvdata(pdev, NULL);
err_free_pdata:
/* If we have no platform_data, we allocated pdata dynamically. */
if (!dev_get_platdata(&pdev->dev))
kfree(pdata);
return error;
}
static int gpio_keys_polled_remove(struct platform_device *pdev)
{
struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev);
const struct gpio_keys_platform_data *pdata = bdev->pdata;
int i;
input_unregister_polled_device(bdev->poll_dev);
for (i = 0; i < pdata->nbuttons; i++)
gpio_free(pdata->buttons[i].gpio);
input_free_polled_device(bdev->poll_dev);
/*
* If we had no platform_data, we allocated pdata dynamically and
* must free it here.
*/
if (!dev_get_platdata(&pdev->dev))
kfree(pdata);
kfree(bdev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver gpio_keys_polled_driver = {
.probe = gpio_keys_polled_probe,
.remove = gpio_keys_polled_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_keys_polled_of_match),
},
};
module_platform_driver(gpio_keys_polled_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_DESCRIPTION("Polled GPIO Buttons driver");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@@ -0,0 +1,586 @@
/*
* Generic linux-input device driver for keyboard devices
*
* Copyright (c) 2001 Brian S. Julin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
*
* References:
* HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
*
*/
#include <linux/hil.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
#define PREFIX "HIL: "
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
MODULE_DESCRIPTION("HIL keyboard/mouse driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("serio:ty03pr25id00ex*"); /* HIL keyboard */
MODULE_ALIAS("serio:ty03pr25id0Fex*"); /* HIL mouse */
#define HIL_PACKET_MAX_LENGTH 16
#define HIL_KBD_SET1_UPBIT 0x01
#define HIL_KBD_SET1_SHIFT 1
static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
{ HIL_KEYCODES_SET1 };
#define HIL_KBD_SET2_UPBIT 0x01
#define HIL_KBD_SET2_SHIFT 1
/* Set2 is user defined */
#define HIL_KBD_SET3_UPBIT 0x80
#define HIL_KBD_SET3_SHIFT 0
static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly =
{ HIL_KEYCODES_SET3 };
static const char hil_language[][16] = { HIL_LOCALE_MAP };
struct hil_dev {
struct input_dev *dev;
struct serio *serio;
/* Input buffer and index for packets from HIL bus. */
hil_packet data[HIL_PACKET_MAX_LENGTH];
int idx4; /* four counts per packet */
/* Raw device info records from HIL bus, see hil.h for fields. */
char idd[HIL_PACKET_MAX_LENGTH]; /* DID byte and IDD record */
char rsc[HIL_PACKET_MAX_LENGTH]; /* RSC record */
char exd[HIL_PACKET_MAX_LENGTH]; /* EXD record */
char rnm[HIL_PACKET_MAX_LENGTH + 1]; /* RNM record + NULL term. */
struct completion cmd_done;
bool is_pointer;
/* Extra device details needed for pointing devices. */
unsigned int nbtn, naxes;
unsigned int btnmap[7];
};
static bool hil_dev_is_command_response(hil_packet p)
{
if ((p & ~HIL_CMDCT_POL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
return false;
if ((p & ~HIL_CMDCT_RPL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
return false;
return true;
}
static void hil_dev_handle_command_response(struct hil_dev *dev)
{
hil_packet p;
char *buf;
int i, idx;
idx = dev->idx4 / 4;
p = dev->data[idx - 1];
switch (p & HIL_PKT_DATA_MASK) {
case HIL_CMD_IDD:
buf = dev->idd;
break;
case HIL_CMD_RSC:
buf = dev->rsc;
break;
case HIL_CMD_EXD:
buf = dev->exd;
break;
case HIL_CMD_RNM:
dev->rnm[HIL_PACKET_MAX_LENGTH] = 0;
buf = dev->rnm;
break;
default:
/* These occur when device isn't present */
if (p != (HIL_ERR_INT | HIL_PKT_CMD)) {
/* Anything else we'd like to know about. */
printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
}
goto out;
}
for (i = 0; i < idx; i++)
buf[i] = dev->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_PACKET_MAX_LENGTH; i++)
buf[i] = 0;
out:
complete(&dev->cmd_done);
}
static void hil_dev_handle_kbd_events(struct hil_dev *kbd)
{
struct input_dev *dev = kbd->dev;
int idx = kbd->idx4 / 4;
int i;
switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
case HIL_POL_CHARTYPE_NONE:
return;
case HIL_POL_CHARTYPE_ASCII:
for (i = 1; i < idx - 1; i++)
input_report_key(dev, kbd->data[i] & 0x7f, 1);
break;
case HIL_POL_CHARTYPE_RSVD1:
case HIL_POL_CHARTYPE_RSVD2:
case HIL_POL_CHARTYPE_BINARY:
for (i = 1; i < idx - 1; i++)
input_report_key(dev, kbd->data[i], 1);
break;
case HIL_POL_CHARTYPE_SET1:
for (i = 1; i < idx - 1; i++) {
unsigned int key = kbd->data[i];
int up = key & HIL_KBD_SET1_UPBIT;
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
input_report_key(dev, key, !up);
}
break;
case HIL_POL_CHARTYPE_SET2:
for (i = 1; i < idx - 1; i++) {
unsigned int key = kbd->data[i];
int up = key & HIL_KBD_SET2_UPBIT;
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
key = key >> HIL_KBD_SET2_SHIFT;
input_report_key(dev, key, !up);
}
break;
case HIL_POL_CHARTYPE_SET3:
for (i = 1; i < idx - 1; i++) {
unsigned int key = kbd->data[i];
int up = key & HIL_KBD_SET3_UPBIT;
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
input_report_key(dev, key, !up);
}
break;
}
input_sync(dev);
}
static void hil_dev_handle_ptr_events(struct hil_dev *ptr)
{
struct input_dev *dev = ptr->dev;
int idx = ptr->idx4 / 4;
hil_packet p = ptr->data[idx - 1];
int i, cnt, laxis;
bool absdev, ax16;
if ((p & HIL_CMDCT_POL) != idx - 1) {
printk(KERN_WARNING PREFIX
"Malformed poll packet %x (idx = %i)\n", p, idx);
return;
}
i = (p & HIL_POL_AXIS_ALT) ? 3 : 0;
laxis = (p & HIL_POL_NUM_AXES_MASK) + i;
ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
for (cnt = 1; i < laxis; i++) {
unsigned int lo, hi, val;
lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
if (absdev) {
val = lo + (hi << 8);
#ifdef TABLET_AUTOADJUST
if (val < input_abs_get_min(dev, ABS_X + i))
input_abs_set_min(dev, ABS_X + i, val);
if (val > input_abs_get_max(dev, ABS_X + i))
input_abs_set_max(dev, ABS_X + i, val);
#endif
if (i % 3)
val = input_abs_get_max(dev, ABS_X + i) - val;
input_report_abs(dev, ABS_X + i, val);
} else {
val = (int) (((int8_t) lo) | ((int8_t) hi << 8));
if (i % 3)
val *= -1;
input_report_rel(dev, REL_X + i, val);
}
}
while (cnt < idx - 1) {
unsigned int btn = ptr->data[cnt++];
int up = btn & 1;
btn &= 0xfe;
if (btn == 0x8e)
continue; /* TODO: proximity == touch? */
if (btn > 0x8c || btn < 0x80)
continue;
btn = (btn - 0x80) >> 1;
btn = ptr->btnmap[btn];
input_report_key(dev, btn, !up);
}
input_sync(dev);
}
static void hil_dev_process_err(struct hil_dev *dev)
{
printk(KERN_WARNING PREFIX "errored HIL packet\n");
dev->idx4 = 0;
complete(&dev->cmd_done); /* just in case somebody is waiting */
}
static irqreturn_t hil_dev_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct hil_dev *dev;
hil_packet packet;
int idx;
dev = serio_get_drvdata(serio);
BUG_ON(dev == NULL);
if (dev->idx4 >= HIL_PACKET_MAX_LENGTH * sizeof(hil_packet)) {
hil_dev_process_err(dev);
goto out;
}
idx = dev->idx4 / 4;
if (!(dev->idx4 % 4))
dev->data[idx] = 0;
packet = dev->data[idx];
packet |= ((hil_packet)data) << ((3 - (dev->idx4 % 4)) * 8);
dev->data[idx] = packet;
/* Records of N 4-byte hil_packets must terminate with a command. */
if ((++dev->idx4 % 4) == 0) {
if ((packet & 0xffff0000) != HIL_ERR_INT) {
hil_dev_process_err(dev);
} else if (packet & HIL_PKT_CMD) {
if (hil_dev_is_command_response(packet))
hil_dev_handle_command_response(dev);
else if (dev->is_pointer)
hil_dev_handle_ptr_events(dev);
else
hil_dev_handle_kbd_events(dev);
dev->idx4 = 0;
}
}
out:
return IRQ_HANDLED;
}
static void hil_dev_disconnect(struct serio *serio)
{
struct hil_dev *dev = serio_get_drvdata(serio);
BUG_ON(dev == NULL);
serio_close(serio);
input_unregister_device(dev->dev);
serio_set_drvdata(serio, NULL);
kfree(dev);
}
static void hil_dev_keyboard_setup(struct hil_dev *kbd)
{
struct input_dev *input_dev = kbd->dev;
uint8_t did = kbd->idd[0];
int i;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
BIT_MASK(LED_SCROLLL);
for (i = 0; i < 128; i++) {
__set_bit(hil_kbd_set1[i], input_dev->keybit);
__set_bit(hil_kbd_set3[i], input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
input_dev->keycodesize = sizeof(hil_kbd_set1[0]);
input_dev->keycode = hil_kbd_set1;
input_dev->name = strlen(kbd->rnm) ? kbd->rnm : "HIL keyboard";
input_dev->phys = "hpkbd/input0";
printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
}
static void hil_dev_pointer_setup(struct hil_dev *ptr)
{
struct input_dev *input_dev = ptr->dev;
uint8_t did = ptr->idd[0];
uint8_t *idd = ptr->idd + 1;
unsigned int naxsets = HIL_IDD_NUM_AXSETS(*idd);
unsigned int i, btntype;
const char *txt;
ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
switch (did & HIL_IDD_DID_TYPE_MASK) {
case HIL_IDD_DID_TYPE_REL:
input_dev->evbit[0] = BIT_MASK(EV_REL);
for (i = 0; i < ptr->naxes; i++)
__set_bit(REL_X + i, input_dev->relbit);
for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++)
__set_bit(REL_X + i, input_dev->relbit);
txt = "relative";
break;
case HIL_IDD_DID_TYPE_ABS:
input_dev->evbit[0] = BIT_MASK(EV_ABS);
for (i = 0; i < ptr->naxes; i++)
input_set_abs_params(input_dev, ABS_X + i,
0, HIL_IDD_AXIS_MAX(idd, i), 0, 0);
for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++)
input_set_abs_params(input_dev, ABS_X + i,
0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0);
#ifdef TABLET_AUTOADJUST
for (i = 0; i < ABS_MAX; i++) {
int diff = input_abs_get_max(input_dev, ABS_X + i) / 10;
input_abs_set_min(input_dev, ABS_X + i,
input_abs_get_min(input_dev, ABS_X + i) + diff);
input_abs_set_max(input_dev, ABS_X + i,
input_abs_get_max(input_dev, ABS_X + i) - diff);
}
#endif
txt = "absolute";
break;
default:
BUG();
}
ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
if (ptr->nbtn)
input_dev->evbit[0] |= BIT_MASK(EV_KEY);
btntype = BTN_MISC;
if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
#ifdef TABLET_SIMULATES_MOUSE
btntype = BTN_TOUCH;
#else
btntype = BTN_DIGI;
#endif
if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
btntype = BTN_TOUCH;
if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
btntype = BTN_MOUSE;
for (i = 0; i < ptr->nbtn; i++) {
__set_bit(btntype | i, input_dev->keybit);
ptr->btnmap[i] = btntype | i;
}
if (btntype == BTN_MOUSE) {
/* Swap buttons 2 and 3 */
ptr->btnmap[1] = BTN_MIDDLE;
ptr->btnmap[2] = BTN_RIGHT;
}
input_dev->name = strlen(ptr->rnm) ? ptr->rnm : "HIL pointer device";
printk(KERN_INFO PREFIX
"HIL pointer device found (did: 0x%02x, axis: %s)\n",
did, txt);
printk(KERN_INFO PREFIX
"HIL pointer has %i buttons and %i sets of %i axes\n",
ptr->nbtn, naxsets, ptr->naxes);
}
static int hil_dev_connect(struct serio *serio, struct serio_driver *drv)
{
struct hil_dev *dev;
struct input_dev *input_dev;
uint8_t did, *idd;
int error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
input_dev = input_allocate_device();
if (!dev || !input_dev) {
error = -ENOMEM;
goto bail0;
}
dev->serio = serio;
dev->dev = input_dev;
error = serio_open(serio, drv);
if (error)
goto bail0;
serio_set_drvdata(serio, dev);
/* Get device info. MLC driver supplies devid/status/etc. */
init_completion(&dev->cmd_done);
serio_write(serio, 0);
serio_write(serio, 0);
serio_write(serio, HIL_PKT_CMD >> 8);
serio_write(serio, HIL_CMD_IDD);
error = wait_for_completion_killable(&dev->cmd_done);
if (error)
goto bail1;
init_completion(&dev->cmd_done);
serio_write(serio, 0);
serio_write(serio, 0);
serio_write(serio, HIL_PKT_CMD >> 8);
serio_write(serio, HIL_CMD_RSC);
error = wait_for_completion_killable(&dev->cmd_done);
if (error)
goto bail1;
init_completion(&dev->cmd_done);
serio_write(serio, 0);
serio_write(serio, 0);
serio_write(serio, HIL_PKT_CMD >> 8);
serio_write(serio, HIL_CMD_RNM);
error = wait_for_completion_killable(&dev->cmd_done);
if (error)
goto bail1;
init_completion(&dev->cmd_done);
serio_write(serio, 0);
serio_write(serio, 0);
serio_write(serio, HIL_PKT_CMD >> 8);
serio_write(serio, HIL_CMD_EXD);
error = wait_for_completion_killable(&dev->cmd_done);
if (error)
goto bail1;
did = dev->idd[0];
idd = dev->idd + 1;
switch (did & HIL_IDD_DID_TYPE_MASK) {
case HIL_IDD_DID_TYPE_KB_INTEGRAL:
case HIL_IDD_DID_TYPE_KB_ITF:
case HIL_IDD_DID_TYPE_KB_RSVD:
case HIL_IDD_DID_TYPE_CHAR:
if (HIL_IDD_NUM_BUTTONS(idd) ||
HIL_IDD_NUM_AXES_PER_SET(*idd)) {
printk(KERN_INFO PREFIX
"combo devices are not supported.\n");
goto bail1;
}
dev->is_pointer = false;
hil_dev_keyboard_setup(dev);
break;
case HIL_IDD_DID_TYPE_REL:
case HIL_IDD_DID_TYPE_ABS:
dev->is_pointer = true;
hil_dev_pointer_setup(dev);
break;
default:
goto bail1;
}
input_dev->id.bustype = BUS_HIL;
input_dev->id.vendor = PCI_VENDOR_ID_HP;
input_dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
input_dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
input_dev->dev.parent = &serio->dev;
if (!dev->is_pointer) {
serio_write(serio, 0);
serio_write(serio, 0);
serio_write(serio, HIL_PKT_CMD >> 8);
/* Enable Keyswitch Autorepeat 1 */
serio_write(serio, HIL_CMD_EK1);
/* No need to wait for completion */
}
error = input_register_device(input_dev);
if (error)
goto bail1;
return 0;
bail1:
serio_close(serio);
serio_set_drvdata(serio, NULL);
bail0:
input_free_device(input_dev);
kfree(dev);
return error;
}
static struct serio_device_id hil_dev_ids[] = {
{
.type = SERIO_HIL_MLC,
.proto = SERIO_HIL,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, hil_dev_ids);
static struct serio_driver hil_serio_drv = {
.driver = {
.name = "hil_dev",
},
.description = "HP HIL keyboard/mouse/tablet driver",
.id_table = hil_dev_ids,
.connect = hil_dev_connect,
.disconnect = hil_dev_disconnect,
.interrupt = hil_dev_interrupt
};
module_serio_driver(hil_serio_drv);

View File

@@ -0,0 +1,398 @@
/*
* linux/drivers/hil/hilkbd.c
*
* Copyright (C) 1998 Philip Blundell <philb@gnu.org>
* Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
* Copyright (C) 1999-2007 Helge Deller <deller@gmx.de>
*
* Very basic HP Human Interface Loop (HIL) driver.
* This driver handles the keyboard on HP300 (m68k) and on some
* HP700 (parisc) series machines.
*
*
* This file is subject to the terms and conditions of the GNU General Public
* License version 2. See the file COPYING in the main directory of this
* archive for more details.
*/
#include <linux/pci_ids.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/hil.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <asm/irq.h>
#ifdef CONFIG_HP300
#include <asm/hwtest.h>
#endif
MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
MODULE_LICENSE("GPL v2");
#if defined(CONFIG_PARISC)
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/parisc-device.h>
static unsigned long hil_base; /* HPA for the HIL device */
static unsigned int hil_irq;
#define HILBASE hil_base /* HPPA (parisc) port address */
#define HIL_DATA 0x800
#define HIL_CMD 0x801
#define HIL_IRQ hil_irq
#define hil_readb(p) gsc_readb(p)
#define hil_writeb(v,p) gsc_writeb((v),(p))
#elif defined(CONFIG_HP300)
#define HILBASE 0xf0428000UL /* HP300 (m68k) port address */
#define HIL_DATA 0x1
#define HIL_CMD 0x3
#define HIL_IRQ 2
#define hil_readb(p) readb(p)
#define hil_writeb(v,p) writeb((v),(p))
#else
#error "HIL is not supported on this platform"
#endif
/* HIL helper functions */
#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
#define hil_status() (hil_readb(HILBASE + HIL_CMD))
#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
/* HIL constants */
#define HIL_BUSY 0x02
#define HIL_DATA_RDY 0x01
#define HIL_SETARD 0xA0 /* set auto-repeat delay */
#define HIL_SETARR 0xA2 /* set auto-repeat rate */
#define HIL_SETTONE 0xA3 /* set tone generator */
#define HIL_CNMT 0xB2 /* clear nmi */
#define HIL_INTON 0x5C /* Turn on interrupts. */
#define HIL_INTOFF 0x5D /* Turn off interrupts. */
#define HIL_READKBDSADR 0xF9
#define HIL_WRITEKBDSADR 0xE9
static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
{ HIL_KEYCODES_SET1 };
/* HIL structure */
static struct {
struct input_dev *dev;
unsigned int curdev;
unsigned char s;
unsigned char c;
int valid;
unsigned char data[16];
unsigned int ptr;
spinlock_t lock;
void *dev_id; /* native bus device */
} hil_dev;
static void poll_finished(void)
{
int down;
int key;
unsigned char scode;
switch (hil_dev.data[0]) {
case 0x40:
down = (hil_dev.data[1] & 1) == 0;
scode = hil_dev.data[1] >> 1;
key = hphilkeyb_keycode[scode];
input_report_key(hil_dev.dev, key, down);
break;
}
hil_dev.curdev = 0;
}
static inline void handle_status(unsigned char s, unsigned char c)
{
if (c & 0x8) {
/* End of block */
if (c & 0x10)
poll_finished();
} else {
if (c & 0x10) {
if (hil_dev.curdev)
poll_finished(); /* just in case */
hil_dev.curdev = c & 7;
hil_dev.ptr = 0;
}
}
}
static inline void handle_data(unsigned char s, unsigned char c)
{
if (hil_dev.curdev) {
hil_dev.data[hil_dev.ptr++] = c;
hil_dev.ptr &= 15;
}
}
/* handle HIL interrupts */
static irqreturn_t hil_interrupt(int irq, void *handle)
{
unsigned char s, c;
s = hil_status();
c = hil_read_data();
switch (s >> 4) {
case 0x5:
handle_status(s, c);
break;
case 0x6:
handle_data(s, c);
break;
case 0x4:
hil_dev.s = s;
hil_dev.c = c;
mb();
hil_dev.valid = 1;
break;
}
return IRQ_HANDLED;
}
/* send a command to the HIL */
static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
{
unsigned long flags;
spin_lock_irqsave(&hil_dev.lock, flags);
while (hil_busy())
/* wait */;
hil_command(cmd);
while (len--) {
while (hil_busy())
/* wait */;
hil_write_data(*(data++));
}
spin_unlock_irqrestore(&hil_dev.lock, flags);
}
/* initialize HIL */
static int hil_keyb_init(void)
{
unsigned char c;
unsigned int i, kbid;
wait_queue_head_t hil_wait;
int err;
if (hil_dev.dev)
return -ENODEV; /* already initialized */
init_waitqueue_head(&hil_wait);
spin_lock_init(&hil_dev.lock);
hil_dev.dev = input_allocate_device();
if (!hil_dev.dev)
return -ENOMEM;
err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
if (err) {
printk(KERN_ERR "HIL: Can't get IRQ\n");
goto err1;
}
/* Turn on interrupts */
hil_do(HIL_INTON, NULL, 0);
/* Look for keyboards */
hil_dev.valid = 0; /* clear any pending data */
hil_do(HIL_READKBDSADR, NULL, 0);
wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ);
if (!hil_dev.valid)
printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
c = hil_dev.c;
hil_dev.valid = 0;
if (c == 0) {
kbid = -1;
printk(KERN_WARNING "HIL: no keyboard present\n");
} else {
kbid = ffz(~c);
printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid);
}
/* set it to raw mode */
c = 0;
hil_do(HIL_WRITEKBDSADR, &c, 1);
for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
if (hphilkeyb_keycode[i] != KEY_RESERVED)
__set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
BIT_MASK(LED_SCROLLL);
hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]);
hil_dev.dev->keycode = hphilkeyb_keycode;
hil_dev.dev->name = "HIL keyboard";
hil_dev.dev->phys = "hpkbd/input0";
hil_dev.dev->id.bustype = BUS_HIL;
hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP;
hil_dev.dev->id.product = 0x0001;
hil_dev.dev->id.version = 0x0010;
err = input_register_device(hil_dev.dev);
if (err) {
printk(KERN_ERR "HIL: Can't register device\n");
goto err2;
}
printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
return 0;
err2:
hil_do(HIL_INTOFF, NULL, 0);
free_irq(HIL_IRQ, hil_dev.dev_id);
err1:
input_free_device(hil_dev.dev);
hil_dev.dev = NULL;
return err;
}
static void hil_keyb_exit(void)
{
if (HIL_IRQ)
free_irq(HIL_IRQ, hil_dev.dev_id);
/* Turn off interrupts */
hil_do(HIL_INTOFF, NULL, 0);
input_unregister_device(hil_dev.dev);
hil_dev.dev = NULL;
}
#if defined(CONFIG_PARISC)
static int hil_probe_chip(struct parisc_device *dev)
{
/* Only allow one HIL keyboard */
if (hil_dev.dev)
return -ENODEV;
if (!dev->irq) {
printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n",
(void *)dev->hpa.start);
return -ENODEV;
}
hil_base = dev->hpa.start;
hil_irq = dev->irq;
hil_dev.dev_id = dev;
printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
return hil_keyb_init();
}
static int hil_remove_chip(struct parisc_device *dev)
{
hil_keyb_exit();
return 0;
}
static struct parisc_device_id hil_tbl[] = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
{ 0, }
};
#if 0
/* Disabled to avoid conflicts with the HP SDC HIL drivers */
MODULE_DEVICE_TABLE(parisc, hil_tbl);
#endif
static struct parisc_driver hil_driver = {
.name = "hil",
.id_table = hil_tbl,
.probe = hil_probe_chip,
.remove = hil_remove_chip,
};
static int __init hil_init(void)
{
return register_parisc_driver(&hil_driver);
}
static void __exit hil_exit(void)
{
unregister_parisc_driver(&hil_driver);
}
#else /* !CONFIG_PARISC */
static int __init hil_init(void)
{
int error;
/* Only allow one HIL keyboard */
if (hil_dev.dev)
return -EBUSY;
if (!MACH_IS_HP300)
return -ENODEV;
if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
printk(KERN_ERR "HIL: hardware register was not found\n");
return -ENODEV;
}
if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
printk(KERN_ERR "HIL: IOPORT region already used\n");
return -EIO;
}
error = hil_keyb_init();
if (error) {
release_region(HILBASE + HIL_DATA, 2);
return error;
}
return 0;
}
static void __exit hil_exit(void)
{
hil_keyb_exit();
release_region(HILBASE + HIL_DATA, 2);
}
#endif /* CONFIG_PARISC */
module_init(hil_init);
module_exit(hil_exit);

View File

@@ -0,0 +1,110 @@
/*
* drivers/input/keyboard/hpps2atkbd.h
*
* Copyright (c) 2004 Helge Deller <deller@gmx.de>
* Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
* Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
* Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
*
* HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
/* Is the keyboard an RDI PrecisionBook? */
#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES
# define CONFLICT(x,y) x
#else
# define CONFLICT(x,y) y
#endif
/* sadly RDI (Tadpole) decided to ship a different keyboard layout
than HP for their PS/2 laptop keyboard which leads to conflicting
keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
HP: RDI: */
#define C_07 CONFLICT( KEY_F12, KEY_F1 )
#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL )
#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK )
#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL )
#define C_61 CONFLICT( KEY_102ND, KEY_LEFT )
/* Raw SET 2 scancode table */
/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07,
/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2,
/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3,
/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4,
/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5,
/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6,
/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7,
/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8,
/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9,
/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10,
/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ,
/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK,
/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT,
/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP,
/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK,
/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND,
/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA,
/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA,
/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE,
/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE,
/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED,
/* These are offset for escaped keycodes: */
/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
#undef CONFLICT
#undef C_07
#undef C_11
#undef C_14
#undef C_58
#undef C_61

View File

@@ -0,0 +1,601 @@
/*
* Driver for the IMX keypad port.
* Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* <<Power management needs to be implemented>>.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/timer.h>
/*
* Keypad Controller registers (halfword)
*/
#define KPCR 0x00 /* Keypad Control Register */
#define KPSR 0x02 /* Keypad Status Register */
#define KBD_STAT_KPKD (0x1 << 0) /* Key Press Interrupt Status bit (w1c) */
#define KBD_STAT_KPKR (0x1 << 1) /* Key Release Interrupt Status bit (w1c) */
#define KBD_STAT_KDSC (0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/
#define KBD_STAT_KRSS (0x1 << 3) /* Key Release Synch Status bit (w1c)*/
#define KBD_STAT_KDIE (0x1 << 8) /* Key Depress Interrupt Enable Status bit */
#define KBD_STAT_KRIE (0x1 << 9) /* Key Release Interrupt Enable */
#define KBD_STAT_KPPEN (0x1 << 10) /* Keypad Clock Enable */
#define KDDR 0x04 /* Keypad Data Direction Register */
#define KPDR 0x06 /* Keypad Data Register */
#define MAX_MATRIX_KEY_ROWS 8
#define MAX_MATRIX_KEY_COLS 8
#define MATRIX_ROW_SHIFT 3
#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
struct imx_keypad {
struct clk *clk;
struct input_dev *input_dev;
void __iomem *mmio_base;
int irq;
struct timer_list check_matrix_timer;
/*
* The matrix is stable only if no changes are detected after
* IMX_KEYPAD_SCANS_FOR_STABILITY scans
*/
#define IMX_KEYPAD_SCANS_FOR_STABILITY 3
int stable_count;
bool enabled;
/* Masks for enabled rows/cols */
unsigned short rows_en_mask;
unsigned short cols_en_mask;
unsigned short keycodes[MAX_MATRIX_KEY_NUM];
/*
* Matrix states:
* -stable: achieved after a complete debounce process.
* -unstable: used in the debouncing process.
*/
unsigned short matrix_stable_state[MAX_MATRIX_KEY_COLS];
unsigned short matrix_unstable_state[MAX_MATRIX_KEY_COLS];
};
/* Scan the matrix and return the new state in *matrix_volatile_state. */
static void imx_keypad_scan_matrix(struct imx_keypad *keypad,
unsigned short *matrix_volatile_state)
{
int col;
unsigned short reg_val;
for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
if ((keypad->cols_en_mask & (1 << col)) == 0)
continue;
/*
* Discharge keypad capacitance:
* 2. write 1s on column data.
* 3. configure columns as totem-pole to discharge capacitance.
* 4. configure columns as open-drain.
*/
reg_val = readw(keypad->mmio_base + KPDR);
reg_val |= 0xff00;
writew(reg_val, keypad->mmio_base + KPDR);
reg_val = readw(keypad->mmio_base + KPCR);
reg_val &= ~((keypad->cols_en_mask & 0xff) << 8);
writew(reg_val, keypad->mmio_base + KPCR);
udelay(2);
reg_val = readw(keypad->mmio_base + KPCR);
reg_val |= (keypad->cols_en_mask & 0xff) << 8;
writew(reg_val, keypad->mmio_base + KPCR);
/*
* 5. Write a single column to 0, others to 1.
* 6. Sample row inputs and save data.
* 7. Repeat steps 2 - 6 for remaining columns.
*/
reg_val = readw(keypad->mmio_base + KPDR);
reg_val &= ~(1 << (8 + col));
writew(reg_val, keypad->mmio_base + KPDR);
/*
* Delay added to avoid propagating the 0 from column to row
* when scanning.
*/
udelay(5);
/*
* 1s in matrix_volatile_state[col] means key pressures
* throw data from non enabled rows.
*/
reg_val = readw(keypad->mmio_base + KPDR);
matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask;
}
/*
* Return in standby mode:
* 9. write 0s to columns
*/
reg_val = readw(keypad->mmio_base + KPDR);
reg_val &= 0x00ff;
writew(reg_val, keypad->mmio_base + KPDR);
}
/*
* Compare the new matrix state (volatile) with the stable one stored in
* keypad->matrix_stable_state and fire events if changes are detected.
*/
static void imx_keypad_fire_events(struct imx_keypad *keypad,
unsigned short *matrix_volatile_state)
{
struct input_dev *input_dev = keypad->input_dev;
int row, col;
for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
unsigned short bits_changed;
int code;
if ((keypad->cols_en_mask & (1 << col)) == 0)
continue; /* Column is not enabled */
bits_changed = keypad->matrix_stable_state[col] ^
matrix_volatile_state[col];
if (bits_changed == 0)
continue; /* Column does not contain changes */
for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
if ((keypad->rows_en_mask & (1 << row)) == 0)
continue; /* Row is not enabled */
if ((bits_changed & (1 << row)) == 0)
continue; /* Row does not contain changes */
code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, keypad->keycodes[code],
matrix_volatile_state[col] & (1 << row));
dev_dbg(&input_dev->dev, "Event code: %d, val: %d",
keypad->keycodes[code],
matrix_volatile_state[col] & (1 << row));
}
}
input_sync(input_dev);
}
/*
* imx_keypad_check_for_events is the timer handler.
*/
static void imx_keypad_check_for_events(unsigned long data)
{
struct imx_keypad *keypad = (struct imx_keypad *) data;
unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS];
unsigned short reg_val;
bool state_changed, is_zero_matrix;
int i;
memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state));
imx_keypad_scan_matrix(keypad, matrix_volatile_state);
state_changed = false;
for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {
if ((keypad->cols_en_mask & (1 << i)) == 0)
continue;
if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) {
state_changed = true;
break;
}
}
/*
* If the matrix state is changed from the previous scan
* (Re)Begin the debouncing process, saving the new state in
* keypad->matrix_unstable_state.
* else
* Increase the count of number of scans with a stable state.
*/
if (state_changed) {
memcpy(keypad->matrix_unstable_state, matrix_volatile_state,
sizeof(matrix_volatile_state));
keypad->stable_count = 0;
} else
keypad->stable_count++;
/*
* If the matrix is not as stable as we want reschedule scan
* in the near future.
*/
if (keypad->stable_count < IMX_KEYPAD_SCANS_FOR_STABILITY) {
mod_timer(&keypad->check_matrix_timer,
jiffies + msecs_to_jiffies(10));
return;
}
/*
* If the matrix state is stable, fire the events and save the new
* stable state. Note, if the matrix is kept stable for longer
* (keypad->stable_count > IMX_KEYPAD_SCANS_FOR_STABILITY) all
* events have already been generated.
*/
if (keypad->stable_count == IMX_KEYPAD_SCANS_FOR_STABILITY) {
imx_keypad_fire_events(keypad, matrix_volatile_state);
memcpy(keypad->matrix_stable_state, matrix_volatile_state,
sizeof(matrix_volatile_state));
}
is_zero_matrix = true;
for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {
if (matrix_volatile_state[i] != 0) {
is_zero_matrix = false;
break;
}
}
if (is_zero_matrix) {
/*
* All keys have been released. Enable only the KDI
* interrupt for future key presses (clear the KDI
* status bit and its sync chain before that).
*/
reg_val = readw(keypad->mmio_base + KPSR);
reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC;
writew(reg_val, keypad->mmio_base + KPSR);
reg_val = readw(keypad->mmio_base + KPSR);
reg_val |= KBD_STAT_KDIE;
reg_val &= ~KBD_STAT_KRIE;
writew(reg_val, keypad->mmio_base + KPSR);
} else {
/*
* Some keys are still pressed. Schedule a rescan in
* attempt to detect multiple key presses and enable
* the KRI interrupt to react quickly to key release
* event.
*/
mod_timer(&keypad->check_matrix_timer,
jiffies + msecs_to_jiffies(60));
reg_val = readw(keypad->mmio_base + KPSR);
reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS;
writew(reg_val, keypad->mmio_base + KPSR);
reg_val = readw(keypad->mmio_base + KPSR);
reg_val |= KBD_STAT_KRIE;
reg_val &= ~KBD_STAT_KDIE;
writew(reg_val, keypad->mmio_base + KPSR);
}
}
static irqreturn_t imx_keypad_irq_handler(int irq, void *dev_id)
{
struct imx_keypad *keypad = dev_id;
unsigned short reg_val;
reg_val = readw(keypad->mmio_base + KPSR);
/* Disable both interrupt types */
reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
/* Clear interrupts status bits */
reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
writew(reg_val, keypad->mmio_base + KPSR);
if (keypad->enabled) {
/* The matrix is supposed to be changed */
keypad->stable_count = 0;
/* Schedule the scanning procedure near in the future */
mod_timer(&keypad->check_matrix_timer,
jiffies + msecs_to_jiffies(2));
}
return IRQ_HANDLED;
}
static void imx_keypad_config(struct imx_keypad *keypad)
{
unsigned short reg_val;
/*
* Include enabled rows in interrupt generation (KPCR[7:0])
* Configure keypad columns as open-drain (KPCR[15:8])
*/
reg_val = readw(keypad->mmio_base + KPCR);
reg_val |= keypad->rows_en_mask & 0xff; /* rows */
reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */
writew(reg_val, keypad->mmio_base + KPCR);
/* Write 0's to KPDR[15:8] (Colums) */
reg_val = readw(keypad->mmio_base + KPDR);
reg_val &= 0x00ff;
writew(reg_val, keypad->mmio_base + KPDR);
/* Configure columns as output, rows as input (KDDR[15:0]) */
writew(0xff00, keypad->mmio_base + KDDR);
/*
* Clear Key Depress and Key Release status bit.
* Clear both synchronizer chain.
*/
reg_val = readw(keypad->mmio_base + KPSR);
reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD |
KBD_STAT_KDSC | KBD_STAT_KRSS;
writew(reg_val, keypad->mmio_base + KPSR);
/* Enable KDI and disable KRI (avoid false release events). */
reg_val |= KBD_STAT_KDIE;
reg_val &= ~KBD_STAT_KRIE;
writew(reg_val, keypad->mmio_base + KPSR);
}
static void imx_keypad_inhibit(struct imx_keypad *keypad)
{
unsigned short reg_val;
/* Inhibit KDI and KRI interrupts. */
reg_val = readw(keypad->mmio_base + KPSR);
reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
writew(reg_val, keypad->mmio_base + KPSR);
/* Colums as open drain and disable all rows */
reg_val = (keypad->cols_en_mask & 0xff) << 8;
writew(reg_val, keypad->mmio_base + KPCR);
}
static void imx_keypad_close(struct input_dev *dev)
{
struct imx_keypad *keypad = input_get_drvdata(dev);
dev_dbg(&dev->dev, ">%s\n", __func__);
/* Mark keypad as being inactive */
keypad->enabled = false;
synchronize_irq(keypad->irq);
del_timer_sync(&keypad->check_matrix_timer);
imx_keypad_inhibit(keypad);
/* Disable clock unit */
clk_disable_unprepare(keypad->clk);
}
static int imx_keypad_open(struct input_dev *dev)
{
struct imx_keypad *keypad = input_get_drvdata(dev);
int error;
dev_dbg(&dev->dev, ">%s\n", __func__);
/* Enable the kpp clock */
error = clk_prepare_enable(keypad->clk);
if (error)
return error;
/* We became active from now */
keypad->enabled = true;
imx_keypad_config(keypad);
/* Sanity control, not all the rows must be actived now. */
if ((readw(keypad->mmio_base + KPDR) & keypad->rows_en_mask) == 0) {
dev_err(&dev->dev,
"too many keys pressed, control pins initialisation\n");
goto open_err;
}
return 0;
open_err:
imx_keypad_close(dev);
return -EIO;
}
#ifdef CONFIG_OF
static struct of_device_id imx_keypad_of_match[] = {
{ .compatible = "fsl,imx21-kpp", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
#endif
static int imx_keypad_probe(struct platform_device *pdev)
{
const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
struct imx_keypad *keypad;
struct input_dev *input_dev;
struct resource *res;
int irq, error, i, row, col;
if (!keymap_data && !pdev->dev.of_node) {
dev_err(&pdev->dev, "no keymap defined\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq defined in platform data\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no I/O memory defined in platform data\n");
return -EINVAL;
}
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev) {
dev_err(&pdev->dev, "failed to allocate the input device\n");
return -ENOMEM;
}
keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
GFP_KERNEL);
if (!keypad) {
dev_err(&pdev->dev, "not enough memory for driver data\n");
return -ENOMEM;
}
keypad->input_dev = input_dev;
keypad->irq = irq;
keypad->stable_count = 0;
setup_timer(&keypad->check_matrix_timer,
imx_keypad_check_for_events, (unsigned long) keypad);
keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(keypad->mmio_base))
return PTR_ERR(keypad->mmio_base);
keypad->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
return PTR_ERR(keypad->clk);
}
/* Init the Input device */
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
input_dev->open = imx_keypad_open;
input_dev->close = imx_keypad_close;
error = matrix_keypad_build_keymap(keymap_data, NULL,
MAX_MATRIX_KEY_ROWS,
MAX_MATRIX_KEY_COLS,
keypad->keycodes, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
return error;
}
/* Search for rows and cols enabled */
for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
if (keypad->keycodes[i] != KEY_RESERVED) {
keypad->rows_en_mask |= 1 << row;
keypad->cols_en_mask |= 1 << col;
}
}
}
dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
/* Ensure that the keypad will stay dormant until opened */
clk_prepare_enable(keypad->clk);
imx_keypad_inhibit(keypad);
clk_disable_unprepare(keypad->clk);
error = devm_request_irq(&pdev->dev, irq, imx_keypad_irq_handler, 0,
pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return error;
}
/* Register the input device */
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
return error;
}
platform_set_drvdata(pdev, keypad);
device_init_wakeup(&pdev->dev, 1);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int imx_kbd_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_keypad *kbd = platform_get_drvdata(pdev);
struct input_dev *input_dev = kbd->input_dev;
/* imx kbd can wake up system even clock is disabled */
mutex_lock(&input_dev->mutex);
if (input_dev->users)
clk_disable_unprepare(kbd->clk);
mutex_unlock(&input_dev->mutex);
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(kbd->irq);
return 0;
}
static int imx_kbd_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_keypad *kbd = platform_get_drvdata(pdev);
struct input_dev *input_dev = kbd->input_dev;
int ret = 0;
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(kbd->irq);
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
ret = clk_prepare_enable(kbd->clk);
if (ret)
goto err_clk;
}
err_clk:
mutex_unlock(&input_dev->mutex);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume);
static struct platform_driver imx_keypad_driver = {
.driver = {
.name = "imx-keypad",
.owner = THIS_MODULE,
.pm = &imx_kbd_pm_ops,
.of_match_table = of_match_ptr(imx_keypad_of_match),
},
.probe = imx_keypad_probe,
};
module_platform_driver(imx_keypad_driver);
MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>");
MODULE_DESCRIPTION("IMX Keypad Port Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:imx-keypad");

View File

@@ -0,0 +1,268 @@
/*
* drivers/input/keyboard/jornada680_kbd.c
*
* HP Jornada 620/660/680/690 scan keyboard platform driver
* Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
*
* Based on hp680_keyb.c
* Copyright (C) 2006 Paul Mundt
* Copyright (C) 2005 Andriy Skulysh
* Split from drivers/input/keyboard/hp600_keyb.c
* Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table)
* Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/delay.h>
#include <asm/io.h>
#define PCCR 0xa4000104
#define PDCR 0xa4000106
#define PECR 0xa4000108
#define PFCR 0xa400010a
#define PCDR 0xa4000124
#define PDDR 0xa4000126
#define PEDR 0xa4000128
#define PFDR 0xa400012a
#define PGDR 0xa400012c
#define PHDR 0xa400012e
#define PJDR 0xa4000130
#define PKDR 0xa4000132
#define PLDR 0xa4000134
static const unsigned short jornada_scancodes[] = {
/* PTD1 */ KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, KEY_KP5, 0, 0, /* 1 -> 8 */
KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F6, KEY_F4, KEY_F5, /* 9 -> 16 */
/* PTD5 */ KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0, /* 17 -> 24 */
KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N, /* 25 -> 32 */
/* PTD7 */ KEY_KP2, KEY_KP6, KEY_KP3, 0, 0, 0, 0, 0, /* 33 -> 40 */
KEY_F10, KEY_RO, KEY_F9, KEY_KP4, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_LEFTALT, KEY_HANJA, /* 41 -> 48 */
/* PTE0 */ KEY_KATAKANA, KEY_KP0, KEY_GRAVE, 0, KEY_FINANCE, 0, 0, 0, /* 49 -> 56 */
KEY_KPMINUS, KEY_HIRAGANA, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0, /* 57 -> 64 */
/* PTE1 */ KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0, /* 65 -> 72 */
KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H, /* 73 -> 80 */
/* PTE3 */ KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0, 0, /* 81 -> 88 */
0, KEY_LEFTSHIFT, KEY_KP7, KEY_KP9, KEY_KP1, KEY_F11, KEY_KPPLUS, KEY_KPASTERISK, /* 89 -> 96 */
/* PTE6 */ KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0, /* 97 -> 104 */
KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_Y, /* 105 -> 112 */
/* PTE7 */ KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0, /* 113 -> 120 */
KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6, /* 121 -> 128 */
/* **** */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
#define JORNADA_SCAN_SIZE 18
struct jornadakbd {
struct input_polled_dev *poll_dev;
unsigned short keymap[ARRAY_SIZE(jornada_scancodes)];
unsigned char length;
unsigned char old_scan[JORNADA_SCAN_SIZE];
unsigned char new_scan[JORNADA_SCAN_SIZE];
};
static void jornada_parse_kbd(struct jornadakbd *jornadakbd)
{
struct input_dev *input_dev = jornadakbd->poll_dev->input;
unsigned short *keymap = jornadakbd->keymap;
unsigned int sync_me = 0;
unsigned int i, j;
for (i = 0; i < JORNADA_SCAN_SIZE; i++) {
unsigned char new = jornadakbd->new_scan[i];
unsigned char old = jornadakbd->old_scan[i];
unsigned int xor = new ^ old;
if (xor == 0)
continue;
for (j = 0; j < 8; j++) {
unsigned int bit = 1 << j;
if (xor & bit) {
unsigned int scancode = (i << 3) + j;
input_event(input_dev,
EV_MSC, MSC_SCAN, scancode);
input_report_key(input_dev,
keymap[scancode],
!(new & bit));
sync_me = 1;
}
}
}
if (sync_me)
input_sync(input_dev);
}
static void jornada_scan_keyb(unsigned char *s)
{
int i;
unsigned short ec_static, dc_static; /* = UINT16_t */
unsigned char matrix_switch[] = {
0xfd, 0xff, /* PTD1 PD(1) */
0xdf, 0xff, /* PTD5 PD(5) */
0x7f, 0xff, /* PTD7 PD(7) */
0xff, 0xfe, /* PTE0 PE(0) */
0xff, 0xfd, /* PTE1 PE(1) */
0xff, 0xf7, /* PTE3 PE(3) */
0xff, 0xbf, /* PTE6 PE(6) */
0xff, 0x7f, /* PTE7 PE(7) */
}, *t = matrix_switch;
/* PD(x) :
1. 0xcc0c & (1~(1 << (2*(x)+1)))))
2. (0xf0cf & 0xfffff) */
/* PE(x) :
1. 0xcc0c & 0xffff
2. 0xf0cf & (1~(1 << (2*(x)+1))))) */
unsigned short matrix_PDE[] = {
0xcc04, 0xf0cf, /* PD(1) */
0xc40c, 0xf0cf, /* PD(5) */
0x4c0c, 0xf0cf, /* PD(7) */
0xcc0c, 0xf0cd, /* PE(0) */
0xcc0c, 0xf0c7, /* PE(1) */
0xcc0c, 0xf04f, /* PE(3) */
0xcc0c, 0xd0cf, /* PE(6) */
0xcc0c, 0x70cf, /* PE(7) */
}, *y = matrix_PDE;
/* Save these control reg bits */
dc_static = (__raw_readw(PDCR) & (~0xcc0c));
ec_static = (__raw_readw(PECR) & (~0xf0cf));
for (i = 0; i < 8; i++) {
/* disable output for all but the one we want to scan */
__raw_writew((dc_static | *y++), PDCR);
__raw_writew((ec_static | *y++), PECR);
udelay(5);
/* Get scanline row */
__raw_writeb(*t++, PDDR);
__raw_writeb(*t++, PEDR);
udelay(50);
/* Read data */
*s++ = __raw_readb(PCDR);
*s++ = __raw_readb(PFDR);
}
/* Scan no lines */
__raw_writeb(0xff, PDDR);
__raw_writeb(0xff, PEDR);
/* Enable all scanlines */
__raw_writew((dc_static | (0x5555 & 0xcc0c)),PDCR);
__raw_writew((ec_static | (0x5555 & 0xf0cf)),PECR);
/* Ignore extra keys and events */
*s++ = __raw_readb(PGDR);
*s++ = __raw_readb(PHDR);
}
static void jornadakbd680_poll(struct input_polled_dev *dev)
{
struct jornadakbd *jornadakbd = dev->private;
jornada_scan_keyb(jornadakbd->new_scan);
jornada_parse_kbd(jornadakbd);
memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE);
}
static int jornada680kbd_probe(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd;
struct input_polled_dev *poll_dev;
struct input_dev *input_dev;
int i, error;
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
if (!jornadakbd)
return -ENOMEM;
poll_dev = input_allocate_polled_device();
if (!poll_dev) {
error = -ENOMEM;
goto failed;
}
platform_set_drvdata(pdev, jornadakbd);
jornadakbd->poll_dev = poll_dev;
memcpy(jornadakbd->keymap, jornada_scancodes,
sizeof(jornadakbd->keymap));
poll_dev->private = jornadakbd;
poll_dev->poll = jornadakbd680_poll;
poll_dev->poll_interval = 50; /* msec */
input_dev = poll_dev->input;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->name = "HP Jornada 680 keyboard";
input_dev->phys = "jornadakbd/input0";
input_dev->keycode = jornadakbd->keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes);
input_dev->dev.parent = &pdev->dev;
input_dev->id.bustype = BUS_HOST;
for (i = 0; i < 128; i++)
if (jornadakbd->keymap[i])
__set_bit(jornadakbd->keymap[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
error = input_register_polled_device(jornadakbd->poll_dev);
if (error)
goto failed;
return 0;
failed:
printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n",
error);
platform_set_drvdata(pdev, NULL);
input_free_polled_device(poll_dev);
kfree(jornadakbd);
return error;
}
static int jornada680kbd_remove(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
input_unregister_polled_device(jornadakbd->poll_dev);
input_free_polled_device(jornadakbd->poll_dev);
kfree(jornadakbd);
return 0;
}
static struct platform_driver jornada680kbd_driver = {
.driver = {
.name = "jornada680_kbd",
.owner = THIS_MODULE,
},
.probe = jornada680kbd_probe,
.remove = jornada680kbd_remove,
};
module_platform_driver(jornada680kbd_driver);
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:jornada680_kbd");

View File

@@ -0,0 +1,178 @@
/*
* drivers/input/keyboard/jornada720_kbd.c
*
* HP Jornada 720 keyboard platform driver
*
* Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@Gmail.com>
*
* Copyright (C) 2006 jornada 720 kbd driver by
Filip Zyzniewsk <Filip.Zyzniewski@tefnet.plX
* based on (C) 2004 jornada 720 kbd driver by
Alex Lange <chicken@handhelds.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <mach/jornada720.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
MODULE_LICENSE("GPL v2");
static unsigned short jornada_std_keymap[128] = { /* ROW */
0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* #1 */
KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, /* -> */
0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, /* #2 */
KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0, /* -> */
0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, /* #3 */
KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0, /* -> */
0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, /* #4 */
KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0, /* -> */
0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, /* #5 */
KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0, /* -> */
0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0, /* #6 */
KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE, /* -> */
0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK, /* -> */
KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0, /* -> */
0, 0, 0, KEY_POWER, /* -> */
};
struct jornadakbd {
unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)];
struct input_dev *input;
};
static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
struct input_dev *input = jornadakbd->input;
u8 count, kbd_data, scan_code;
/* startup ssp with spinlock */
jornada_ssp_start();
if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
printk(KERN_DEBUG
"jornada720_kbd: "
"GetKeycode command failed with ETIMEDOUT, "
"flushed bus\n");
} else {
/* How many keycodes are waiting for us? */
count = jornada_ssp_byte(TXDUMMY);
/* Lets drag them out one at a time */
while (count--) {
/* Exchange TxDummy for location (keymap[kbddata]) */
kbd_data = jornada_ssp_byte(TXDUMMY);
scan_code = kbd_data & 0x7f;
input_event(input, EV_MSC, MSC_SCAN, scan_code);
input_report_key(input, jornadakbd->keymap[scan_code],
!(kbd_data & 0x80));
input_sync(input);
}
}
/* release spinlock and turn off ssp */
jornada_ssp_end();
return IRQ_HANDLED;
};
static int jornada720_kbd_probe(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd;
struct input_dev *input_dev;
int i, err;
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!jornadakbd || !input_dev) {
err = -ENOMEM;
goto fail1;
}
platform_set_drvdata(pdev, jornadakbd);
memcpy(jornadakbd->keymap, jornada_std_keymap,
sizeof(jornada_std_keymap));
jornadakbd->input = input_dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->name = "HP Jornada 720 keyboard";
input_dev->phys = "jornadakbd/input0";
input_dev->keycode = jornadakbd->keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap);
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++)
__set_bit(jornadakbd->keymap[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
err = request_irq(IRQ_GPIO0,
jornada720_kbd_interrupt,
IRQF_TRIGGER_FALLING,
"jornadakbd", pdev);
if (err) {
printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
goto fail1;
}
err = input_register_device(jornadakbd->input);
if (err)
goto fail2;
return 0;
fail2: /* IRQ, DEVICE, MEMORY */
free_irq(IRQ_GPIO0, pdev);
fail1: /* DEVICE, MEMORY */
platform_set_drvdata(pdev, NULL);
input_free_device(input_dev);
kfree(jornadakbd);
return err;
};
static int jornada720_kbd_remove(struct platform_device *pdev)
{
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
free_irq(IRQ_GPIO0, pdev);
platform_set_drvdata(pdev, NULL);
input_unregister_device(jornadakbd->input);
kfree(jornadakbd);
return 0;
}
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:jornada720_kbd");
static struct platform_driver jornada720_kbd_driver = {
.driver = {
.name = "jornada720_kbd",
.owner = THIS_MODULE,
},
.probe = jornada720_kbd_probe,
.remove = jornada720_kbd_remove,
};
module_platform_driver(jornada720_kbd_driver);

View File

@@ -0,0 +1,734 @@
/*
* Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
*/
/*
* LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
*/
/*
* DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
* and VAXstations, but can also be used on any standard RS232 with an
* adaptor).
*
* DISCLAIMER: This works for _me_. If you break anything by using the
* information given below, I will _not_ be liable!
*
* RJ10 pinout: To DE9: Or DB25:
* 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD)
* 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND)
* 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD)
* 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
*
* Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
* RJ10, it's like this:
*
* __=__ Hold the plug in front of you, cable downwards,
* /___/| nose is hidden behind the plug. Now, pin 1 is at
* |1234|| the left side, pin 4 at the right and 2 and 3 are
* |IIII|| in between, of course:)
* | ||
* |____|/
* || So the adaptor consists of three connected cables
* || for data transmission (RxD and TxD) and signal ground.
* Additionally, you have to get +12V from somewhere.
* Most easily, you'll get that from a floppy or HDD power connector.
* It's the yellow cable there (black is ground and red is +5V).
*
* The keyboard and all the commands it understands are documented in
* "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
* document is LK201 specific, but LK401 is mostly compatible. It comes
* up in LK201 mode and doesn't report any of the additional keys it
* has. These need to be switched on with the LK_CMD_ENABLE_LK401
* command. You'll find this document (scanned .pdf file) on MANX,
* a search engine specific to DEC documentation. Try
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/
/*
* 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/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#define DRIVER_DESC "LK keyboard driver"
MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Known parameters:
* bell_volume
* keyclick_volume
* ctrlclick_volume
*
* Please notice that there's not yet an API to set these at runtime.
*/
static int bell_volume = 100; /* % */
module_param(bell_volume, int, 0);
MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%");
static int keyclick_volume = 100; /* % */
module_param(keyclick_volume, int, 0);
MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%");
static int ctrlclick_volume = 100; /* % */
module_param(ctrlclick_volume, int, 0);
MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
static int lk201_compose_is_alt;
module_param(lk201_compose_is_alt, int, 0);
MODULE_PARM_DESC(lk201_compose_is_alt,
"If set non-zero, LK201' Compose key will act as an Alt key");
#undef LKKBD_DEBUG
#ifdef LKKBD_DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...) do {} while (0)
#endif
/* LED control */
#define LK_LED_WAIT 0x81
#define LK_LED_COMPOSE 0x82
#define LK_LED_SHIFTLOCK 0x84
#define LK_LED_SCROLLLOCK 0x88
#define LK_CMD_LED_ON 0x13
#define LK_CMD_LED_OFF 0x11
/* Mode control */
#define LK_MODE_DOWN 0x80
#define LK_MODE_AUTODOWN 0x82
#define LK_MODE_UPDOWN 0x86
#define LK_CMD_SET_MODE(mode, div) ((mode) | ((div) << 3))
/* Misc commands */
#define LK_CMD_ENABLE_KEYCLICK 0x1b
#define LK_CMD_DISABLE_KEYCLICK 0x99
#define LK_CMD_DISABLE_BELL 0xa1
#define LK_CMD_SOUND_BELL 0xa7
#define LK_CMD_ENABLE_BELL 0x23
#define LK_CMD_DISABLE_CTRCLICK 0xb9
#define LK_CMD_ENABLE_CTRCLICK 0xbb
#define LK_CMD_SET_DEFAULTS 0xd3
#define LK_CMD_POWERCYCLE_RESET 0xfd
#define LK_CMD_ENABLE_LK401 0xe9
#define LK_CMD_REQUEST_ID 0xab
/* Misc responses from keyboard */
#define LK_STUCK_KEY 0x3d
#define LK_SELFTEST_FAILED 0x3e
#define LK_ALL_KEYS_UP 0xb3
#define LK_METRONOME 0xb4
#define LK_OUTPUT_ERROR 0xb5
#define LK_INPUT_ERROR 0xb6
#define LK_KBD_LOCKED 0xb7
#define LK_KBD_TEST_MODE_ACK 0xb8
#define LK_PREFIX_KEY_DOWN 0xb9
#define LK_MODE_CHANGE_ACK 0xba
#define LK_RESPONSE_RESERVED 0xbb
#define LK_NUM_KEYCODES 256
#define LK_NUM_IGNORE_BYTES 6
static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = {
[0x56] = KEY_F1,
[0x57] = KEY_F2,
[0x58] = KEY_F3,
[0x59] = KEY_F4,
[0x5a] = KEY_F5,
[0x64] = KEY_F6,
[0x65] = KEY_F7,
[0x66] = KEY_F8,
[0x67] = KEY_F9,
[0x68] = KEY_F10,
[0x71] = KEY_F11,
[0x72] = KEY_F12,
[0x73] = KEY_F13,
[0x74] = KEY_F14,
[0x7c] = KEY_F15,
[0x7d] = KEY_F16,
[0x80] = KEY_F17,
[0x81] = KEY_F18,
[0x82] = KEY_F19,
[0x83] = KEY_F20,
[0x8a] = KEY_FIND,
[0x8b] = KEY_INSERT,
[0x8c] = KEY_DELETE,
[0x8d] = KEY_SELECT,
[0x8e] = KEY_PAGEUP,
[0x8f] = KEY_PAGEDOWN,
[0x92] = KEY_KP0,
[0x94] = KEY_KPDOT,
[0x95] = KEY_KPENTER,
[0x96] = KEY_KP1,
[0x97] = KEY_KP2,
[0x98] = KEY_KP3,
[0x99] = KEY_KP4,
[0x9a] = KEY_KP5,
[0x9b] = KEY_KP6,
[0x9c] = KEY_KPCOMMA,
[0x9d] = KEY_KP7,
[0x9e] = KEY_KP8,
[0x9f] = KEY_KP9,
[0xa0] = KEY_KPMINUS,
[0xa1] = KEY_PROG1,
[0xa2] = KEY_PROG2,
[0xa3] = KEY_PROG3,
[0xa4] = KEY_PROG4,
[0xa7] = KEY_LEFT,
[0xa8] = KEY_RIGHT,
[0xa9] = KEY_DOWN,
[0xaa] = KEY_UP,
[0xab] = KEY_RIGHTSHIFT,
[0xac] = KEY_LEFTALT,
[0xad] = KEY_COMPOSE, /* Right Compose, that is. */
[0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
[0xaf] = KEY_LEFTCTRL,
[0xb0] = KEY_CAPSLOCK,
[0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
[0xb2] = KEY_RIGHTALT,
[0xbc] = KEY_BACKSPACE,
[0xbd] = KEY_ENTER,
[0xbe] = KEY_TAB,
[0xbf] = KEY_ESC,
[0xc0] = KEY_1,
[0xc1] = KEY_Q,
[0xc2] = KEY_A,
[0xc3] = KEY_Z,
[0xc5] = KEY_2,
[0xc6] = KEY_W,
[0xc7] = KEY_S,
[0xc8] = KEY_X,
[0xc9] = KEY_102ND,
[0xcb] = KEY_3,
[0xcc] = KEY_E,
[0xcd] = KEY_D,
[0xce] = KEY_C,
[0xd0] = KEY_4,
[0xd1] = KEY_R,
[0xd2] = KEY_F,
[0xd3] = KEY_V,
[0xd4] = KEY_SPACE,
[0xd6] = KEY_5,
[0xd7] = KEY_T,
[0xd8] = KEY_G,
[0xd9] = KEY_B,
[0xdb] = KEY_6,
[0xdc] = KEY_Y,
[0xdd] = KEY_H,
[0xde] = KEY_N,
[0xe0] = KEY_7,
[0xe1] = KEY_U,
[0xe2] = KEY_J,
[0xe3] = KEY_M,
[0xe5] = KEY_8,
[0xe6] = KEY_I,
[0xe7] = KEY_K,
[0xe8] = KEY_COMMA,
[0xea] = KEY_9,
[0xeb] = KEY_O,
[0xec] = KEY_L,
[0xed] = KEY_DOT,
[0xef] = KEY_0,
[0xf0] = KEY_P,
[0xf2] = KEY_SEMICOLON,
[0xf3] = KEY_SLASH,
[0xf5] = KEY_EQUAL,
[0xf6] = KEY_RIGHTBRACE,
[0xf7] = KEY_BACKSLASH,
[0xf9] = KEY_MINUS,
[0xfa] = KEY_LEFTBRACE,
[0xfb] = KEY_APOSTROPHE,
};
#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \
if (test_bit(LED, (LK)->dev->led)) \
VAR_ON |= BITS; \
else \
VAR_OFF |= BITS; \
} while (0)
/*
* Per-keyboard data
*/
struct lkkbd {
unsigned short keycode[LK_NUM_KEYCODES];
int ignore_bytes;
unsigned char id[LK_NUM_IGNORE_BYTES];
struct input_dev *dev;
struct serio *serio;
struct work_struct tq;
char name[64];
char phys[32];
char type;
int bell_volume;
int keyclick_volume;
int ctrlclick_volume;
};
#ifdef LKKBD_DEBUG
/*
* Responses from the keyboard and mapping back to their names.
*/
static struct {
unsigned char value;
unsigned char *name;
} lk_response[] = {
#define RESPONSE(x) { .value = (x), .name = #x, }
RESPONSE(LK_STUCK_KEY),
RESPONSE(LK_SELFTEST_FAILED),
RESPONSE(LK_ALL_KEYS_UP),
RESPONSE(LK_METRONOME),
RESPONSE(LK_OUTPUT_ERROR),
RESPONSE(LK_INPUT_ERROR),
RESPONSE(LK_KBD_LOCKED),
RESPONSE(LK_KBD_TEST_MODE_ACK),
RESPONSE(LK_PREFIX_KEY_DOWN),
RESPONSE(LK_MODE_CHANGE_ACK),
RESPONSE(LK_RESPONSE_RESERVED),
#undef RESPONSE
};
static unsigned char *response_name(unsigned char value)
{
int i;
for (i = 0; i < ARRAY_SIZE(lk_response); i++)
if (lk_response[i].value == value)
return lk_response[i].name;
return "<unknown>";
}
#endif /* LKKBD_DEBUG */
/*
* Calculate volume parameter byte for a given volume.
*/
static unsigned char volume_to_hw(int volume_percent)
{
unsigned char ret = 0;
if (volume_percent < 0)
volume_percent = 0;
if (volume_percent > 100)
volume_percent = 100;
if (volume_percent >= 0)
ret = 7;
if (volume_percent >= 13) /* 12.5 */
ret = 6;
if (volume_percent >= 25)
ret = 5;
if (volume_percent >= 38) /* 37.5 */
ret = 4;
if (volume_percent >= 50)
ret = 3;
if (volume_percent >= 63) /* 62.5 */
ret = 2; /* This is the default volume */
if (volume_percent >= 75)
ret = 1;
if (volume_percent >= 88) /* 87.5 */
ret = 0;
ret |= 0x80;
return ret;
}
static void lkkbd_detection_done(struct lkkbd *lk)
{
int i;
/*
* Reset setting for Compose key. Let Compose be KEY_COMPOSE.
*/
lk->keycode[0xb1] = KEY_COMPOSE;
/*
* Print keyboard name and modify Compose=Alt on user's request.
*/
switch (lk->id[4]) {
case 1:
strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
if (lk201_compose_is_alt)
lk->keycode[0xb1] = KEY_LEFTALT;
break;
case 2:
strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
break;
default:
strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
printk(KERN_ERR
"lkkbd: keyboard on %s is unknown, please report to "
"Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
printk(KERN_ERR "lkkbd: keyboard ID'ed as:");
for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
printk(" 0x%02x", lk->id[i]);
printk("\n");
break;
}
printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
lk->phys, lk->name);
/*
* Report errors during keyboard boot-up.
*/
switch (lk->id[2]) {
case 0x00:
/* All okay */
break;
case LK_STUCK_KEY:
printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n",
lk->phys);
break;
case LK_SELFTEST_FAILED:
printk(KERN_ERR
"lkkbd: Selftest failed on keyboard at %s, "
"keyboard may not work properly\n", lk->phys);
break;
default:
printk(KERN_ERR
"lkkbd: Unknown error %02x on keyboard at %s\n",
lk->id[2], lk->phys);
break;
}
/*
* Try to hint user if there's a stuck key.
*/
if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
printk(KERN_ERR
"Scancode of stuck key is 0x%02x, keycode is 0x%04x\n",
lk->id[3], lk->keycode[lk->id[3]]);
}
/*
* lkkbd_interrupt() is called by the low level driver when a character
* is received.
*/
static irqreturn_t lkkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct lkkbd *lk = serio_get_drvdata(serio);
struct input_dev *input_dev = lk->dev;
unsigned int keycode;
int i;
DBG(KERN_INFO "Got byte 0x%02x\n", data);
if (lk->ignore_bytes > 0) {
DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name);
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
if (lk->ignore_bytes == 0)
lkkbd_detection_done(lk);
return IRQ_HANDLED;
}
switch (data) {
case LK_ALL_KEYS_UP:
for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++)
input_report_key(input_dev, lk->keycode[i], 0);
input_sync(input_dev);
break;
case 0x01:
DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n");
lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
schedule_work(&lk->tq);
break;
case LK_METRONOME:
case LK_OUTPUT_ERROR:
case LK_INPUT_ERROR:
case LK_KBD_LOCKED:
case LK_KBD_TEST_MODE_ACK:
case LK_PREFIX_KEY_DOWN:
case LK_MODE_CHANGE_ACK:
case LK_RESPONSE_RESERVED:
DBG(KERN_INFO "Got %s and don't know how to handle...\n",
response_name(data));
break;
default:
keycode = lk->keycode[data];
if (keycode != KEY_RESERVED) {
input_report_key(input_dev, keycode,
!test_bit(keycode, input_dev->key));
input_sync(input_dev);
} else {
printk(KERN_WARNING
"%s: Unknown key with scancode 0x%02x on %s.\n",
__FILE__, data, lk->name);
}
}
return IRQ_HANDLED;
}
static void lkkbd_toggle_leds(struct lkkbd *lk)
{
struct serio *serio = lk->serio;
unsigned char leds_on = 0;
unsigned char leds_off = 0;
CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
if (leds_on != 0) {
serio_write(serio, LK_CMD_LED_ON);
serio_write(serio, leds_on);
}
if (leds_off != 0) {
serio_write(serio, LK_CMD_LED_OFF);
serio_write(serio, leds_off);
}
}
static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on)
{
struct serio *serio = lk->serio;
if (on) {
DBG("%s: Activating key clicks\n", __func__);
serio_write(serio, LK_CMD_ENABLE_KEYCLICK);
serio_write(serio, volume_to_hw(lk->keyclick_volume));
serio_write(serio, LK_CMD_ENABLE_CTRCLICK);
serio_write(serio, volume_to_hw(lk->ctrlclick_volume));
} else {
DBG("%s: Deactivating key clicks\n", __func__);
serio_write(serio, LK_CMD_DISABLE_KEYCLICK);
serio_write(serio, LK_CMD_DISABLE_CTRCLICK);
}
}
/*
* lkkbd_event() handles events from the input module.
*/
static int lkkbd_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct lkkbd *lk = input_get_drvdata(dev);
switch (type) {
case EV_LED:
lkkbd_toggle_leds(lk);
return 0;
case EV_SND:
switch (code) {
case SND_CLICK:
lkkbd_toggle_keyclick(lk, value);
return 0;
case SND_BELL:
if (value != 0)
serio_write(lk->serio, LK_CMD_SOUND_BELL);
return 0;
}
break;
default:
printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n",
__func__, type, code, value);
}
return -1;
}
/*
* lkkbd_reinit() sets leds and beeps to a state the computer remembers they
* were in.
*/
static void lkkbd_reinit(struct work_struct *work)
{
struct lkkbd *lk = container_of(work, struct lkkbd, tq);
int division;
/* Ask for ID */
serio_write(lk->serio, LK_CMD_REQUEST_ID);
/* Reset parameters */
serio_write(lk->serio, LK_CMD_SET_DEFAULTS);
/* Set LEDs */
lkkbd_toggle_leds(lk);
/*
* Try to activate extended LK401 mode. This command will
* only work with a LK401 keyboard and grants access to
* LAlt, RAlt, RCompose and RShift.
*/
serio_write(lk->serio, LK_CMD_ENABLE_LK401);
/* Set all keys to UPDOWN mode */
for (division = 1; division <= 14; division++)
serio_write(lk->serio,
LK_CMD_SET_MODE(LK_MODE_UPDOWN, division));
/* Enable bell and set volume */
serio_write(lk->serio, LK_CMD_ENABLE_BELL);
serio_write(lk->serio, volume_to_hw(lk->bell_volume));
/* Enable/disable keyclick (and possibly set volume) */
lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd));
/* Sound the bell if needed */
if (test_bit(SND_BELL, lk->dev->snd))
serio_write(lk->serio, LK_CMD_SOUND_BELL);
}
/*
* lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
*/
static int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct lkkbd *lk;
struct input_dev *input_dev;
int i;
int err;
lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!lk || !input_dev) {
err = -ENOMEM;
goto fail1;
}
lk->serio = serio;
lk->dev = input_dev;
INIT_WORK(&lk->tq, lkkbd_reinit);
lk->bell_volume = bell_volume;
lk->keyclick_volume = keyclick_volume;
lk->ctrlclick_volume = ctrlclick_volume;
memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
input_dev->name = lk->name;
input_dev->phys = lk->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_LKKBD;
input_dev->id.product = 0;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_dev->event = lkkbd_event;
input_set_drvdata(input_dev, lk);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_LED, input_dev->evbit);
__set_bit(EV_SND, input_dev->evbit);
__set_bit(EV_REP, input_dev->evbit);
__set_bit(LED_CAPSL, input_dev->ledbit);
__set_bit(LED_SLEEP, input_dev->ledbit);
__set_bit(LED_COMPOSE, input_dev->ledbit);
__set_bit(LED_SCROLLL, input_dev->ledbit);
__set_bit(SND_BELL, input_dev->sndbit);
__set_bit(SND_CLICK, input_dev->sndbit);
input_dev->keycode = lk->keycode;
input_dev->keycodesize = sizeof(lk->keycode[0]);
input_dev->keycodemax = ARRAY_SIZE(lk->keycode);
for (i = 0; i < LK_NUM_KEYCODES; i++)
__set_bit(lk->keycode[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
serio_set_drvdata(serio, lk);
err = serio_open(serio, drv);
if (err)
goto fail2;
err = input_register_device(lk->dev);
if (err)
goto fail3;
serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET);
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(lk);
return err;
}
/*
* lkkbd_disconnect() unregisters and closes behind us.
*/
static void lkkbd_disconnect(struct serio *serio)
{
struct lkkbd *lk = serio_get_drvdata(serio);
input_get_device(lk->dev);
input_unregister_device(lk->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_put_device(lk->dev);
kfree(lk);
}
static struct serio_device_id lkkbd_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_LKKBD,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids);
static struct serio_driver lkkbd_drv = {
.driver = {
.name = "lkkbd",
},
.description = DRIVER_DESC,
.id_table = lkkbd_serio_ids,
.connect = lkkbd_connect,
.disconnect = lkkbd_disconnect,
.interrupt = lkkbd_interrupt,
};
module_serio_driver(lkkbd_drv);

View File

@@ -0,0 +1,861 @@
/*
* drivers/i2c/chips/lm8323.c
*
* Copyright (C) 2007-2009 Nokia Corporation
*
* Written by Daniel Stone <daniel.stone@nokia.com>
* Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
*
* Updated by Felipe Balbi <felipe.balbi@nokia.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 (version 2 of the License only).
*
* 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/i2c.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/pm.h>
#include <linux/i2c/lm8323.h>
#include <linux/slab.h>
/* Commands to send to the chip. */
#define LM8323_CMD_READ_ID 0x80 /* Read chip ID. */
#define LM8323_CMD_WRITE_CFG 0x81 /* Set configuration item. */
#define LM8323_CMD_READ_INT 0x82 /* Get interrupt status. */
#define LM8323_CMD_RESET 0x83 /* Reset, same as external one */
#define LM8323_CMD_WRITE_PORT_SEL 0x85 /* Set GPIO in/out. */
#define LM8323_CMD_WRITE_PORT_STATE 0x86 /* Set GPIO pullup. */
#define LM8323_CMD_READ_PORT_SEL 0x87 /* Get GPIO in/out. */
#define LM8323_CMD_READ_PORT_STATE 0x88 /* Get GPIO pullup. */
#define LM8323_CMD_READ_FIFO 0x89 /* Read byte from FIFO. */
#define LM8323_CMD_RPT_READ_FIFO 0x8a /* Read FIFO (no increment). */
#define LM8323_CMD_SET_ACTIVE 0x8b /* Set active time. */
#define LM8323_CMD_READ_ERR 0x8c /* Get error status. */
#define LM8323_CMD_READ_ROTATOR 0x8e /* Read rotator status. */
#define LM8323_CMD_SET_DEBOUNCE 0x8f /* Set debouncing time. */
#define LM8323_CMD_SET_KEY_SIZE 0x90 /* Set keypad size. */
#define LM8323_CMD_READ_KEY_SIZE 0x91 /* Get keypad size. */
#define LM8323_CMD_READ_CFG 0x92 /* Get configuration item. */
#define LM8323_CMD_WRITE_CLOCK 0x93 /* Set clock config. */
#define LM8323_CMD_READ_CLOCK 0x94 /* Get clock config. */
#define LM8323_CMD_PWM_WRITE 0x95 /* Write PWM script. */
#define LM8323_CMD_START_PWM 0x96 /* Start PWM engine. */
#define LM8323_CMD_STOP_PWM 0x97 /* Stop PWM engine. */
/* Interrupt status. */
#define INT_KEYPAD 0x01 /* Key event. */
#define INT_ROTATOR 0x02 /* Rotator event. */
#define INT_ERROR 0x08 /* Error: use CMD_READ_ERR. */
#define INT_NOINIT 0x10 /* Lost configuration. */
#define INT_PWM1 0x20 /* PWM1 stopped. */
#define INT_PWM2 0x40 /* PWM2 stopped. */
#define INT_PWM3 0x80 /* PWM3 stopped. */
/* Errors (signalled by INT_ERROR, read with CMD_READ_ERR). */
#define ERR_BADPAR 0x01 /* Bad parameter. */
#define ERR_CMDUNK 0x02 /* Unknown command. */
#define ERR_KEYOVR 0x04 /* Too many keys pressed. */
#define ERR_FIFOOVER 0x40 /* FIFO overflow. */
/* Configuration keys (CMD_{WRITE,READ}_CFG). */
#define CFG_MUX1SEL 0x01 /* Select MUX1_OUT input. */
#define CFG_MUX1EN 0x02 /* Enable MUX1_OUT. */
#define CFG_MUX2SEL 0x04 /* Select MUX2_OUT input. */
#define CFG_MUX2EN 0x08 /* Enable MUX2_OUT. */
#define CFG_PSIZE 0x20 /* Package size (must be 0). */
#define CFG_ROTEN 0x40 /* Enable rotator. */
/* Clock settings (CMD_{WRITE,READ}_CLOCK). */
#define CLK_RCPWM_INTERNAL 0x00
#define CLK_RCPWM_EXTERNAL 0x03
#define CLK_SLOWCLKEN 0x08 /* Enable 32.768kHz clock. */
#define CLK_SLOWCLKOUT 0x40 /* Enable slow pulse output. */
/* The possible addresses corresponding to CONFIG1 and CONFIG2 pin wirings. */
#define LM8323_I2C_ADDR00 (0x84 >> 1) /* 1000 010x */
#define LM8323_I2C_ADDR01 (0x86 >> 1) /* 1000 011x */
#define LM8323_I2C_ADDR10 (0x88 >> 1) /* 1000 100x */
#define LM8323_I2C_ADDR11 (0x8A >> 1) /* 1000 101x */
/* Key event fifo length */
#define LM8323_FIFO_LEN 15
/* Commands for PWM engine; feed in with PWM_WRITE. */
/* Load ramp counter from duty cycle field (range 0 - 0xff). */
#define PWM_SET(v) (0x4000 | ((v) & 0xff))
/* Go to start of script. */
#define PWM_GOTOSTART 0x0000
/*
* Stop engine (generates interrupt). If reset is 1, clear the program
* counter, else leave it.
*/
#define PWM_END(reset) (0xc000 | (!!(reset) << 11))
/*
* Ramp. If s is 1, divide clock by 512, else divide clock by 16.
* Take t clock scales (up to 63) per step, for n steps (up to 126).
* If u is set, ramp up, else ramp down.
*/
#define PWM_RAMP(s, t, n, u) ((!!(s) << 14) | ((t) & 0x3f) << 8 | \
((n) & 0x7f) | ((u) ? 0 : 0x80))
/*
* Loop (i.e. jump back to pos) for a given number of iterations (up to 63).
* If cnt is zero, execute until PWM_END is encountered.
*/
#define PWM_LOOP(cnt, pos) (0xa000 | (((cnt) & 0x3f) << 7) | \
((pos) & 0x3f))
/*
* Wait for trigger. Argument is a mask of channels, shifted by the channel
* number, e.g. 0xa for channels 3 and 1. Note that channels are numbered
* from 1, not 0.
*/
#define PWM_WAIT_TRIG(chans) (0xe000 | (((chans) & 0x7) << 6))
/* Send trigger. Argument is same as PWM_WAIT_TRIG. */
#define PWM_SEND_TRIG(chans) (0xe000 | ((chans) & 0x7))
struct lm8323_pwm {
int id;
int fade_time;
int brightness;
int desired_brightness;
bool enabled;
bool running;
/* pwm lock */
struct mutex lock;
struct work_struct work;
struct led_classdev cdev;
struct lm8323_chip *chip;
};
struct lm8323_chip {
/* device lock */
struct mutex lock;
struct i2c_client *client;
struct input_dev *idev;
bool kp_enabled;
bool pm_suspend;
unsigned keys_down;
char phys[32];
unsigned short keymap[LM8323_KEYMAP_SIZE];
int size_x;
int size_y;
int debounce_time;
int active_time;
struct lm8323_pwm pwm[LM8323_NUM_PWMS];
};
#define client_to_lm8323(c) container_of(c, struct lm8323_chip, client)
#define dev_to_lm8323(d) container_of(d, struct lm8323_chip, client->dev)
#define cdev_to_pwm(c) container_of(c, struct lm8323_pwm, cdev)
#define work_to_pwm(w) container_of(w, struct lm8323_pwm, work)
#define LM8323_MAX_DATA 8
/*
* To write, we just access the chip's address in write mode, and dump the
* command and data out on the bus. The command byte and data are taken as
* sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA.
*/
static int lm8323_write(struct lm8323_chip *lm, int len, ...)
{
int ret, i;
va_list ap;
u8 data[LM8323_MAX_DATA];
va_start(ap, len);
if (unlikely(len > LM8323_MAX_DATA)) {
dev_err(&lm->client->dev, "tried to send %d bytes\n", len);
va_end(ap);
return 0;
}
for (i = 0; i < len; i++)
data[i] = va_arg(ap, int);
va_end(ap);
/*
* If the host is asleep while we send the data, we can get a NACK
* back while it wakes up, so try again, once.
*/
ret = i2c_master_send(lm->client, data, len);
if (unlikely(ret == -EREMOTEIO))
ret = i2c_master_send(lm->client, data, len);
if (unlikely(ret != len))
dev_err(&lm->client->dev, "sent %d bytes of %d total\n",
len, ret);
return ret;
}
/*
* To read, we first send the command byte to the chip and end the transaction,
* then access the chip in read mode, at which point it will send the data.
*/
static int lm8323_read(struct lm8323_chip *lm, u8 cmd, u8 *buf, int len)
{
int ret;
/*
* If the host is asleep while we send the byte, we can get a NACK
* back while it wakes up, so try again, once.
*/
ret = i2c_master_send(lm->client, &cmd, 1);
if (unlikely(ret == -EREMOTEIO))
ret = i2c_master_send(lm->client, &cmd, 1);
if (unlikely(ret != 1)) {
dev_err(&lm->client->dev, "sending read cmd 0x%02x failed\n",
cmd);
return 0;
}
ret = i2c_master_recv(lm->client, buf, len);
if (unlikely(ret != len))
dev_err(&lm->client->dev, "wanted %d bytes, got %d\n",
len, ret);
return ret;
}
/*
* Set the chip active time (idle time before it enters halt).
*/
static void lm8323_set_active_time(struct lm8323_chip *lm, int time)
{
lm8323_write(lm, 2, LM8323_CMD_SET_ACTIVE, time >> 2);
}
/*
* The signals are AT-style: the low 7 bits are the keycode, and the top
* bit indicates the state (1 for down, 0 for up).
*/
static inline u8 lm8323_whichkey(u8 event)
{
return event & 0x7f;
}
static inline int lm8323_ispress(u8 event)
{
return (event & 0x80) ? 1 : 0;
}
static void process_keys(struct lm8323_chip *lm)
{
u8 event;
u8 key_fifo[LM8323_FIFO_LEN + 1];
int old_keys_down = lm->keys_down;
int ret;
int i = 0;
/*
* Read all key events from the FIFO at once. Next READ_FIFO clears the
* FIFO even if we didn't read all events previously.
*/
ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN);
if (ret < 0) {
dev_err(&lm->client->dev, "Failed reading fifo \n");
return;
}
key_fifo[ret] = 0;
while ((event = key_fifo[i++])) {
u8 key = lm8323_whichkey(event);
int isdown = lm8323_ispress(event);
unsigned short keycode = lm->keymap[key];
dev_vdbg(&lm->client->dev, "key 0x%02x %s\n",
key, isdown ? "down" : "up");
if (lm->kp_enabled) {
input_event(lm->idev, EV_MSC, MSC_SCAN, key);
input_report_key(lm->idev, keycode, isdown);
input_sync(lm->idev);
}
if (isdown)
lm->keys_down++;
else
lm->keys_down--;
}
/*
* Errata: We need to ensure that the chip never enters halt mode
* during a keypress, so set active time to 0. When it's released,
* we can enter halt again, so set the active time back to normal.
*/
if (!old_keys_down && lm->keys_down)
lm8323_set_active_time(lm, 0);
if (old_keys_down && !lm->keys_down)
lm8323_set_active_time(lm, lm->active_time);
}
static void lm8323_process_error(struct lm8323_chip *lm)
{
u8 error;
if (lm8323_read(lm, LM8323_CMD_READ_ERR, &error, 1) == 1) {
if (error & ERR_FIFOOVER)
dev_vdbg(&lm->client->dev, "fifo overflow!\n");
if (error & ERR_KEYOVR)
dev_vdbg(&lm->client->dev,
"more than two keys pressed\n");
if (error & ERR_CMDUNK)
dev_vdbg(&lm->client->dev,
"unknown command submitted\n");
if (error & ERR_BADPAR)
dev_vdbg(&lm->client->dev, "bad command parameter\n");
}
}
static void lm8323_reset(struct lm8323_chip *lm)
{
/* The docs say we must pass 0xAA as the data byte. */
lm8323_write(lm, 2, LM8323_CMD_RESET, 0xAA);
}
static int lm8323_configure(struct lm8323_chip *lm)
{
int keysize = (lm->size_x << 4) | lm->size_y;
int clock = (CLK_SLOWCLKEN | CLK_RCPWM_EXTERNAL);
int debounce = lm->debounce_time >> 2;
int active = lm->active_time >> 2;
/*
* Active time must be greater than the debounce time: if it's
* a close-run thing, give ourselves a 12ms buffer.
*/
if (debounce >= active)
active = debounce + 3;
lm8323_write(lm, 2, LM8323_CMD_WRITE_CFG, 0);
lm8323_write(lm, 2, LM8323_CMD_WRITE_CLOCK, clock);
lm8323_write(lm, 2, LM8323_CMD_SET_KEY_SIZE, keysize);
lm8323_set_active_time(lm, lm->active_time);
lm8323_write(lm, 2, LM8323_CMD_SET_DEBOUNCE, debounce);
lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_STATE, 0xff, 0xff);
lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_SEL, 0, 0);
/*
* Not much we can do about errors at this point, so just hope
* for the best.
*/
return 0;
}
static void pwm_done(struct lm8323_pwm *pwm)
{
mutex_lock(&pwm->lock);
pwm->running = false;
if (pwm->desired_brightness != pwm->brightness)
schedule_work(&pwm->work);
mutex_unlock(&pwm->lock);
}
/*
* Bottom half: handle the interrupt by posting key events, or dealing with
* errors appropriately.
*/
static irqreturn_t lm8323_irq(int irq, void *_lm)
{
struct lm8323_chip *lm = _lm;
u8 ints;
int i;
mutex_lock(&lm->lock);
while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) {
if (likely(ints & INT_KEYPAD))
process_keys(lm);
if (ints & INT_ROTATOR) {
/* We don't currently support the rotator. */
dev_vdbg(&lm->client->dev, "rotator fired\n");
}
if (ints & INT_ERROR) {
dev_vdbg(&lm->client->dev, "error!\n");
lm8323_process_error(lm);
}
if (ints & INT_NOINIT) {
dev_err(&lm->client->dev, "chip lost config; "
"reinitialising\n");
lm8323_configure(lm);
}
for (i = 0; i < LM8323_NUM_PWMS; i++) {
if (ints & (INT_PWM1 << i)) {
dev_vdbg(&lm->client->dev,
"pwm%d engine completed\n", i);
pwm_done(&lm->pwm[i]);
}
}
}
mutex_unlock(&lm->lock);
return IRQ_HANDLED;
}
/*
* Read the chip ID.
*/
static int lm8323_read_id(struct lm8323_chip *lm, u8 *buf)
{
int bytes;
bytes = lm8323_read(lm, LM8323_CMD_READ_ID, buf, 2);
if (unlikely(bytes != 2))
return -EIO;
return 0;
}
static void lm8323_write_pwm_one(struct lm8323_pwm *pwm, int pos, u16 cmd)
{
lm8323_write(pwm->chip, 4, LM8323_CMD_PWM_WRITE, (pos << 2) | pwm->id,
(cmd & 0xff00) >> 8, cmd & 0x00ff);
}
/*
* Write a script into a given PWM engine, concluding with PWM_END.
* If 'kill' is nonzero, the engine will be shut down at the end
* of the script, producing a zero output. Otherwise the engine
* will be kept running at the final PWM level indefinitely.
*/
static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill,
int len, const u16 *cmds)
{
int i;
for (i = 0; i < len; i++)
lm8323_write_pwm_one(pwm, i, cmds[i]);
lm8323_write_pwm_one(pwm, i++, PWM_END(kill));
lm8323_write(pwm->chip, 2, LM8323_CMD_START_PWM, pwm->id);
pwm->running = true;
}
static void lm8323_pwm_work(struct work_struct *work)
{
struct lm8323_pwm *pwm = work_to_pwm(work);
int div512, perstep, steps, hz, up, kill;
u16 pwm_cmds[3];
int num_cmds = 0;
mutex_lock(&pwm->lock);
/*
* Do nothing if we're already at the requested level,
* or previous setting is not yet complete. In the latter
* case we will be called again when the previous PWM script
* finishes.
*/
if (pwm->running || pwm->desired_brightness == pwm->brightness)
goto out;
kill = (pwm->desired_brightness == 0);
up = (pwm->desired_brightness > pwm->brightness);
steps = abs(pwm->desired_brightness - pwm->brightness);
/*
* Convert time (in ms) into a divisor (512 or 16 on a refclk of
* 32768Hz), and number of ticks per step.
*/
if ((pwm->fade_time / steps) > (32768 / 512)) {
div512 = 1;
hz = 32768 / 512;
} else {
div512 = 0;
hz = 32768 / 16;
}
perstep = (hz * pwm->fade_time) / (steps * 1000);
if (perstep == 0)
perstep = 1;
else if (perstep > 63)
perstep = 63;
while (steps) {
int s;
s = min(126, steps);
pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up);
steps -= s;
}
lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds);
pwm->brightness = pwm->desired_brightness;
out:
mutex_unlock(&pwm->lock);
}
static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
struct lm8323_chip *lm = pwm->chip;
mutex_lock(&pwm->lock);
pwm->desired_brightness = brightness;
mutex_unlock(&pwm->lock);
if (in_interrupt()) {
schedule_work(&pwm->work);
} else {
/*
* Schedule PWM work as usual unless we are going into suspend
*/
mutex_lock(&lm->lock);
if (likely(!lm->pm_suspend))
schedule_work(&pwm->work);
else
lm8323_pwm_work(&pwm->work);
mutex_unlock(&lm->lock);
}
}
static ssize_t lm8323_pwm_show_time(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
return sprintf(buf, "%d\n", pwm->fade_time);
}
static ssize_t lm8323_pwm_store_time(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
int ret, time;
ret = kstrtoint(buf, 10, &time);
/* Numbers only, please. */
if (ret)
return ret;
pwm->fade_time = time;
return strlen(buf);
}
static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time);
static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev,
const char *name)
{
struct lm8323_pwm *pwm;
BUG_ON(id > 3);
pwm = &lm->pwm[id - 1];
pwm->id = id;
pwm->fade_time = 0;
pwm->brightness = 0;
pwm->desired_brightness = 0;
pwm->running = false;
pwm->enabled = false;
INIT_WORK(&pwm->work, lm8323_pwm_work);
mutex_init(&pwm->lock);
pwm->chip = lm;
if (name) {
pwm->cdev.name = name;
pwm->cdev.brightness_set = lm8323_pwm_set_brightness;
if (led_classdev_register(dev, &pwm->cdev) < 0) {
dev_err(dev, "couldn't register PWM %d\n", id);
return -1;
}
if (device_create_file(pwm->cdev.dev,
&dev_attr_time) < 0) {
dev_err(dev, "couldn't register time attribute\n");
led_classdev_unregister(&pwm->cdev);
return -1;
}
pwm->enabled = true;
}
return 0;
}
static struct i2c_driver lm8323_i2c_driver;
static ssize_t lm8323_show_disable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm8323_chip *lm = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", !lm->kp_enabled);
}
static ssize_t lm8323_set_disable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm8323_chip *lm = dev_get_drvdata(dev);
int ret;
unsigned int i;
ret = kstrtouint(buf, 10, &i);
mutex_lock(&lm->lock);
lm->kp_enabled = !i;
mutex_unlock(&lm->lock);
return count;
}
static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable);
static int lm8323_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lm8323_platform_data *pdata = client->dev.platform_data;
struct input_dev *idev;
struct lm8323_chip *lm;
int pwm;
int i, err;
unsigned long tmo;
u8 data[2];
if (!pdata || !pdata->size_x || !pdata->size_y) {
dev_err(&client->dev, "missing platform_data\n");
return -EINVAL;
}
if (pdata->size_x > 8) {
dev_err(&client->dev, "invalid x size %d specified\n",
pdata->size_x);
return -EINVAL;
}
if (pdata->size_y > 12) {
dev_err(&client->dev, "invalid y size %d specified\n",
pdata->size_y);
return -EINVAL;
}
lm = kzalloc(sizeof *lm, GFP_KERNEL);
idev = input_allocate_device();
if (!lm || !idev) {
err = -ENOMEM;
goto fail1;
}
lm->client = client;
lm->idev = idev;
mutex_init(&lm->lock);
lm->size_x = pdata->size_x;
lm->size_y = pdata->size_y;
dev_vdbg(&client->dev, "Keypad size: %d x %d\n",
lm->size_x, lm->size_y);
lm->debounce_time = pdata->debounce_time;
lm->active_time = pdata->active_time;
lm8323_reset(lm);
/* Nothing's set up to service the IRQ yet, so just spin for max.
* 100ms until we can configure. */
tmo = jiffies + msecs_to_jiffies(100);
while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) {
if (data[0] & INT_NOINIT)
break;
if (time_after(jiffies, tmo)) {
dev_err(&client->dev,
"timeout waiting for initialisation\n");
break;
}
msleep(1);
}
lm8323_configure(lm);
/* If a true probe check the device */
if (lm8323_read_id(lm, data) != 0) {
dev_err(&client->dev, "device not found\n");
err = -ENODEV;
goto fail1;
}
for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) {
err = init_pwm(lm, pwm + 1, &client->dev,
pdata->pwm_names[pwm]);
if (err < 0)
goto fail2;
}
lm->kp_enabled = true;
err = device_create_file(&client->dev, &dev_attr_disable_kp);
if (err < 0)
goto fail2;
idev->name = pdata->name ? : "LM8323 keypad";
snprintf(lm->phys, sizeof(lm->phys),
"%s/input-kp", dev_name(&client->dev));
idev->phys = lm->phys;
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_MSC);
__set_bit(MSC_SCAN, idev->mscbit);
for (i = 0; i < LM8323_KEYMAP_SIZE; i++) {
__set_bit(pdata->keymap[i], idev->keybit);
lm->keymap[i] = pdata->keymap[i];
}
__clear_bit(KEY_RESERVED, idev->keybit);
if (pdata->repeat)
__set_bit(EV_REP, idev->evbit);
err = input_register_device(idev);
if (err) {
dev_dbg(&client->dev, "error registering input device\n");
goto fail3;
}
err = request_threaded_irq(client->irq, NULL, lm8323_irq,
IRQF_TRIGGER_LOW|IRQF_ONESHOT, "lm8323", lm);
if (err) {
dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
goto fail4;
}
i2c_set_clientdata(client, lm);
device_init_wakeup(&client->dev, 1);
enable_irq_wake(client->irq);
return 0;
fail4:
input_unregister_device(idev);
idev = NULL;
fail3:
device_remove_file(&client->dev, &dev_attr_disable_kp);
fail2:
while (--pwm >= 0)
if (lm->pwm[pwm].enabled) {
device_remove_file(lm->pwm[pwm].cdev.dev,
&dev_attr_time);
led_classdev_unregister(&lm->pwm[pwm].cdev);
}
fail1:
input_free_device(idev);
kfree(lm);
return err;
}
static int lm8323_remove(struct i2c_client *client)
{
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
disable_irq_wake(client->irq);
free_irq(client->irq, lm);
input_unregister_device(lm->idev);
device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
for (i = 0; i < 3; i++)
if (lm->pwm[i].enabled) {
device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time);
led_classdev_unregister(&lm->pwm[i].cdev);
}
kfree(lm);
return 0;
}
#ifdef CONFIG_PM_SLEEP
/*
* We don't need to explicitly suspend the chip, as it already switches off
* when there's no activity.
*/
static int lm8323_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
irq_set_irq_wake(client->irq, 0);
disable_irq(client->irq);
mutex_lock(&lm->lock);
lm->pm_suspend = true;
mutex_unlock(&lm->lock);
for (i = 0; i < 3; i++)
if (lm->pwm[i].enabled)
led_classdev_suspend(&lm->pwm[i].cdev);
return 0;
}
static int lm8323_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
mutex_lock(&lm->lock);
lm->pm_suspend = false;
mutex_unlock(&lm->lock);
for (i = 0; i < 3; i++)
if (lm->pwm[i].enabled)
led_classdev_resume(&lm->pwm[i].cdev);
enable_irq(client->irq);
irq_set_irq_wake(client->irq, 1);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume);
static const struct i2c_device_id lm8323_id[] = {
{ "lm8323", 0 },
{ }
};
static struct i2c_driver lm8323_i2c_driver = {
.driver = {
.name = "lm8323",
.pm = &lm8323_pm_ops,
},
.probe = lm8323_probe,
.remove = lm8323_remove,
.id_table = lm8323_id,
};
MODULE_DEVICE_TABLE(i2c, lm8323_id);
module_i2c_driver(lm8323_i2c_driver);
MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>");
MODULE_AUTHOR("Daniel Stone");
MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
MODULE_DESCRIPTION("LM8323 keypad driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,235 @@
/*
* LM8333 keypad driver
* Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
*
* 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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/input/matrix_keypad.h>
#include <linux/input/lm8333.h>
#define LM8333_FIFO_READ 0x20
#define LM8333_DEBOUNCE 0x22
#define LM8333_READ_INT 0xD0
#define LM8333_ACTIVE 0xE4
#define LM8333_READ_ERROR 0xF0
#define LM8333_KEYPAD_IRQ (1 << 0)
#define LM8333_ERROR_IRQ (1 << 3)
#define LM8333_ERROR_KEYOVR 0x04
#define LM8333_ERROR_FIFOOVR 0x40
#define LM8333_FIFO_TRANSFER_SIZE 16
#define LM8333_NUM_ROWS 8
#define LM8333_NUM_COLS 16
#define LM8333_ROW_SHIFT 4
struct lm8333 {
struct i2c_client *client;
struct input_dev *input;
unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT];
};
/* The accessors try twice because the first access may be needed for wakeup */
#define LM8333_READ_RETRIES 2
int lm8333_read8(struct lm8333 *lm8333, u8 cmd)
{
int retries = 0, ret;
do {
ret = i2c_smbus_read_byte_data(lm8333->client, cmd);
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
return ret;
}
int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val)
{
int retries = 0, ret;
do {
ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val);
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
return ret;
}
int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf)
{
int retries = 0, ret;
do {
ret = i2c_smbus_read_i2c_block_data(lm8333->client,
cmd, len, buf);
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
return ret;
}
static void lm8333_key_handler(struct lm8333 *lm8333)
{
struct input_dev *input = lm8333->input;
u8 keys[LM8333_FIFO_TRANSFER_SIZE];
u8 code, pressed;
int i, ret;
ret = lm8333_read_block(lm8333, LM8333_FIFO_READ,
LM8333_FIFO_TRANSFER_SIZE, keys);
if (ret != LM8333_FIFO_TRANSFER_SIZE) {
dev_err(&lm8333->client->dev,
"Error %d while reading FIFO\n", ret);
return;
}
for (i = 0; i < LM8333_FIFO_TRANSFER_SIZE && keys[i]; i++) {
pressed = keys[i] & 0x80;
code = keys[i] & 0x7f;
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, lm8333->keycodes[code], pressed);
}
input_sync(input);
}
static irqreturn_t lm8333_irq_thread(int irq, void *data)
{
struct lm8333 *lm8333 = data;
u8 status = lm8333_read8(lm8333, LM8333_READ_INT);
if (!status)
return IRQ_NONE;
if (status & LM8333_ERROR_IRQ) {
u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR);
if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) {
u8 dummy[LM8333_FIFO_TRANSFER_SIZE];
lm8333_read_block(lm8333, LM8333_FIFO_READ,
LM8333_FIFO_TRANSFER_SIZE, dummy);
}
dev_err(&lm8333->client->dev, "Got error %02x\n", err);
}
if (status & LM8333_KEYPAD_IRQ)
lm8333_key_handler(lm8333);
return IRQ_HANDLED;
}
static int lm8333_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct lm8333_platform_data *pdata = client->dev.platform_data;
struct lm8333 *lm8333;
struct input_dev *input;
int err, active_time;
if (!pdata)
return -EINVAL;
active_time = pdata->active_time ?: 500;
if (active_time / 3 <= pdata->debounce_time / 3) {
dev_err(&client->dev, "Active time not big enough!\n");
return -EINVAL;
}
lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL);
input = input_allocate_device();
if (!lm8333 || !input) {
err = -ENOMEM;
goto free_mem;
}
lm8333->client = client;
lm8333->input = input;
input->name = client->name;
input->dev.parent = &client->dev;
input->id.bustype = BUS_I2C;
input_set_capability(input, EV_MSC, MSC_SCAN);
err = matrix_keypad_build_keymap(pdata->matrix_data, NULL,
LM8333_NUM_ROWS, LM8333_NUM_COLS,
lm8333->keycodes, input);
if (err)
goto free_mem;
if (pdata->debounce_time) {
err = lm8333_write8(lm8333, LM8333_DEBOUNCE,
pdata->debounce_time / 3);
if (err)
dev_warn(&client->dev, "Unable to set debounce time\n");
}
if (pdata->active_time) {
err = lm8333_write8(lm8333, LM8333_ACTIVE,
pdata->active_time / 3);
if (err)
dev_warn(&client->dev, "Unable to set active time\n");
}
err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"lm8333", lm8333);
if (err)
goto free_mem;
err = input_register_device(input);
if (err)
goto free_irq;
i2c_set_clientdata(client, lm8333);
return 0;
free_irq:
free_irq(client->irq, lm8333);
free_mem:
input_free_device(input);
kfree(lm8333);
return err;
}
static int lm8333_remove(struct i2c_client *client)
{
struct lm8333 *lm8333 = i2c_get_clientdata(client);
free_irq(client->irq, lm8333);
input_unregister_device(lm8333->input);
kfree(lm8333);
return 0;
}
static const struct i2c_device_id lm8333_id[] = {
{ "lm8333", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm8333_id);
static struct i2c_driver lm8333_driver = {
.driver = {
.name = "lm8333",
.owner = THIS_MODULE,
},
.probe = lm8333_probe,
.remove = lm8333_remove,
.id_table = lm8333_id,
};
module_i2c_driver(lm8333_driver);
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
MODULE_DESCRIPTION("LM8333 keyboard driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,362 @@
/*
* LoCoMo keyboard driver for Linux-based ARM PDAs:
* - SHARP Zaurus Collie (SL-5500)
* - SHARP Zaurus Poodle (SL-5600)
*
* Copyright (c) 2005 John Lenz
* Based on from xtkbd.c
*
*
* 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/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <asm/hardware/locomo.h>
#include <asm/irq.h>
MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
MODULE_DESCRIPTION("LoCoMo keyboard driver");
MODULE_LICENSE("GPL");
#define LOCOMOKBD_NUMKEYS 128
#define KEY_ACTIVITY KEY_F16
#define KEY_CONTACT KEY_F18
#define KEY_CENTER KEY_F15
static const unsigned char
locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */
0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */
KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T, /* 50 - 59 */
KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0, /* 60 - 69 */
KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0, /* 70 - 79 */
0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J, /* 80 - 89 */
KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */
0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */
KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */
KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */
};
#define KB_ROWS 16
#define KB_COLS 8
#define KB_ROWMASK(r) (1 << (r))
#define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 )
#define KB_DELAY 8
#define SCAN_INTERVAL (HZ/10)
struct locomokbd {
unsigned char keycode[LOCOMOKBD_NUMKEYS];
struct input_dev *input;
char phys[32];
unsigned long base;
spinlock_t lock;
struct timer_list timer;
unsigned long suspend_jiffies;
unsigned int count_cancel;
};
/* helper functions for reading the keyboard matrix */
static inline void locomokbd_charge_all(unsigned long membase)
{
locomo_writel(0x00FF, membase + LOCOMO_KSC);
}
static inline void locomokbd_activate_all(unsigned long membase)
{
unsigned long r;
locomo_writel(0, membase + LOCOMO_KSC);
r = locomo_readl(membase + LOCOMO_KIC);
r &= 0xFEFF;
locomo_writel(r, membase + LOCOMO_KIC);
}
static inline void locomokbd_activate_col(unsigned long membase, int col)
{
unsigned short nset;
unsigned short nbset;
nset = 0xFF & ~(1 << col);
nbset = (nset << 8) + nset;
locomo_writel(nbset, membase + LOCOMO_KSC);
}
static inline void locomokbd_reset_col(unsigned long membase, int col)
{
unsigned short nbset;
nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
locomo_writel(nbset, membase + LOCOMO_KSC);
}
/*
* The LoCoMo keyboard only generates interrupts when a key is pressed.
* So when a key is pressed, we enable a timer. This timer scans the
* keyboard, and this is how we detect when the key is released.
*/
/* Scan the hardware keyboard and push any changes up through the input layer */
static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
{
unsigned int row, col, rowd;
unsigned long flags;
unsigned int num_pressed;
unsigned long membase = locomokbd->base;
spin_lock_irqsave(&locomokbd->lock, flags);
locomokbd_charge_all(membase);
num_pressed = 0;
for (col = 0; col < KB_COLS; col++) {
locomokbd_activate_col(membase, col);
udelay(KB_DELAY);
rowd = ~locomo_readl(membase + LOCOMO_KIB);
for (row = 0; row < KB_ROWS; row++) {
unsigned int scancode, pressed, key;
scancode = SCANCODE(col, row);
pressed = rowd & KB_ROWMASK(row);
key = locomokbd->keycode[scancode];
input_report_key(locomokbd->input, key, pressed);
if (likely(!pressed))
continue;
num_pressed++;
/* The "Cancel/ESC" key is labeled "On/Off" on
* Collie and Poodle and should suspend the device
* if it was pressed for more than a second. */
if (unlikely(key == KEY_ESC)) {
if (!time_after(jiffies,
locomokbd->suspend_jiffies + HZ))
continue;
if (locomokbd->count_cancel++
!= (HZ/SCAN_INTERVAL + 1))
continue;
input_event(locomokbd->input, EV_PWR,
KEY_SUSPEND, 1);
locomokbd->suspend_jiffies = jiffies;
} else
locomokbd->count_cancel = 0;
}
locomokbd_reset_col(membase, col);
}
locomokbd_activate_all(membase);
input_sync(locomokbd->input);
/* if any keys are pressed, enable the timer */
if (num_pressed)
mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
else
locomokbd->count_cancel = 0;
spin_unlock_irqrestore(&locomokbd->lock, flags);
}
/*
* LoCoMo keyboard interrupt handler.
*/
static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
{
struct locomokbd *locomokbd = dev_id;
u16 r;
r = locomo_readl(locomokbd->base + LOCOMO_KIC);
if ((r & 0x0001) == 0)
return IRQ_HANDLED;
locomo_writel(r & ~0x0100, locomokbd->base + LOCOMO_KIC); /* Ack */
/** wait chattering delay **/
udelay(100);
locomokbd_scankeyboard(locomokbd);
return IRQ_HANDLED;
}
/*
* LoCoMo timer checking for released keys
*/
static void locomokbd_timer_callback(unsigned long data)
{
struct locomokbd *locomokbd = (struct locomokbd *) data;
locomokbd_scankeyboard(locomokbd);
}
static int locomokbd_open(struct input_dev *dev)
{
struct locomokbd *locomokbd = input_get_drvdata(dev);
u16 r;
r = locomo_readl(locomokbd->base + LOCOMO_KIC) | 0x0010;
locomo_writel(r, locomokbd->base + LOCOMO_KIC);
return 0;
}
static void locomokbd_close(struct input_dev *dev)
{
struct locomokbd *locomokbd = input_get_drvdata(dev);
u16 r;
r = locomo_readl(locomokbd->base + LOCOMO_KIC) & ~0x0010;
locomo_writel(r, locomokbd->base + LOCOMO_KIC);
}
static int locomokbd_probe(struct locomo_dev *dev)
{
struct locomokbd *locomokbd;
struct input_dev *input_dev;
int i, err;
locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!locomokbd || !input_dev) {
err = -ENOMEM;
goto err_free_mem;
}
/* try and claim memory region */
if (!request_mem_region((unsigned long) dev->mapbase,
dev->length,
LOCOMO_DRIVER_NAME(dev))) {
err = -EBUSY;
printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
goto err_free_mem;
}
locomo_set_drvdata(dev, locomokbd);
locomokbd->base = (unsigned long) dev->mapbase;
spin_lock_init(&locomokbd->lock);
init_timer(&locomokbd->timer);
locomokbd->timer.function = locomokbd_timer_callback;
locomokbd->timer.data = (unsigned long) locomokbd;
locomokbd->suspend_jiffies = jiffies;
locomokbd->input = input_dev;
strcpy(locomokbd->phys, "locomokbd/input0");
input_dev->name = "LoCoMo keyboard";
input_dev->phys = locomokbd->phys;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->open = locomokbd_open;
input_dev->close = locomokbd_close;
input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
BIT_MASK(EV_PWR);
input_dev->keycode = locomokbd->keycode;
input_dev->keycodesize = sizeof(locomokbd_keycode[0]);
input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode);
input_set_drvdata(input_dev, locomokbd);
memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
set_bit(locomokbd->keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
/* attempt to get the interrupt */
err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
if (err) {
printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
goto err_release_region;
}
err = input_register_device(locomokbd->input);
if (err)
goto err_free_irq;
return 0;
err_free_irq:
free_irq(dev->irq[0], locomokbd);
err_release_region:
release_mem_region((unsigned long) dev->mapbase, dev->length);
locomo_set_drvdata(dev, NULL);
err_free_mem:
input_free_device(input_dev);
kfree(locomokbd);
return err;
}
static int locomokbd_remove(struct locomo_dev *dev)
{
struct locomokbd *locomokbd = locomo_get_drvdata(dev);
free_irq(dev->irq[0], locomokbd);
del_timer_sync(&locomokbd->timer);
input_unregister_device(locomokbd->input);
locomo_set_drvdata(dev, NULL);
release_mem_region((unsigned long) dev->mapbase, dev->length);
kfree(locomokbd);
return 0;
}
static struct locomo_driver keyboard_driver = {
.drv = {
.name = "locomokbd"
},
.devid = LOCOMO_DEVID_KEYBOARD,
.probe = locomokbd_probe,
.remove = locomokbd_remove,
};
static int __init locomokbd_init(void)
{
return locomo_driver_register(&keyboard_driver);
}
static void __exit locomokbd_exit(void)
{
locomo_driver_unregister(&keyboard_driver);
}
module_init(locomokbd_init);
module_exit(locomokbd_exit);

View File

@@ -0,0 +1,395 @@
/*
* NXP LPC32xx SoC Key Scan Interface
*
* Authors:
* Kevin Wells <kevin.wells@nxp.com>
* Roland Stigge <stigge@antcom.de>
*
* Copyright (C) 2010 NXP Semiconductors
* Copyright (C) 2012 Roland Stigge
*
* 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.
*
*
* This controller supports square key matrices from 1x1 up to 8x8
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/input/matrix_keypad.h>
#define DRV_NAME "lpc32xx_keys"
/*
* Key scanner register offsets
*/
#define LPC32XX_KS_DEB(x) ((x) + 0x00)
#define LPC32XX_KS_STATE_COND(x) ((x) + 0x04)
#define LPC32XX_KS_IRQ(x) ((x) + 0x08)
#define LPC32XX_KS_SCAN_CTL(x) ((x) + 0x0C)
#define LPC32XX_KS_FAST_TST(x) ((x) + 0x10)
#define LPC32XX_KS_MATRIX_DIM(x) ((x) + 0x14) /* 1..8 */
#define LPC32XX_KS_DATA(x, y) ((x) + 0x40 + ((y) << 2))
#define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n) ((n) & 0xFF)
#define LPC32XX_KSCAN_SCOND_IN_IDLE 0x0
#define LPC32XX_KSCAN_SCOND_IN_SCANONCE 0x1
#define LPC32XX_KSCAN_SCOND_IN_IRQGEN 0x2
#define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX 0x3
#define LPC32XX_KSCAN_IRQ_PENDING_CLR 0x1
#define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n) ((n) & 0xFF)
#define LPC32XX_KSCAN_FTST_FORCESCANONCE 0x1
#define LPC32XX_KSCAN_FTST_USE32K_CLK 0x2
#define LPC32XX_KSCAN_MSEL_SELECT(n) ((n) & 0xF)
struct lpc32xx_kscan_drv {
struct input_dev *input;
struct clk *clk;
struct resource *iores;
void __iomem *kscan_base;
unsigned int irq;
u32 matrix_sz; /* Size of matrix in XxY, ie. 3 = 3x3 */
u32 deb_clks; /* Debounce clocks (based on 32KHz clock) */
u32 scan_delay; /* Scan delay (based on 32KHz clock) */
unsigned short *keymap; /* Pointer to key map for the scan matrix */
unsigned int row_shift;
u8 lastkeystates[8];
};
static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int col)
{
struct input_dev *input = kscandat->input;
unsigned row, changed, scancode, keycode;
u8 key;
key = readl(LPC32XX_KS_DATA(kscandat->kscan_base, col));
changed = key ^ kscandat->lastkeystates[col];
kscandat->lastkeystates[col] = key;
for (row = 0; changed; row++, changed >>= 1) {
if (changed & 1) {
/* Key state changed, signal an event */
scancode = MATRIX_SCAN_CODE(row, col,
kscandat->row_shift);
keycode = kscandat->keymap[scancode];
input_event(input, EV_MSC, MSC_SCAN, scancode);
input_report_key(input, keycode, key & (1 << row));
}
}
}
static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id)
{
struct lpc32xx_kscan_drv *kscandat = dev_id;
int i;
for (i = 0; i < kscandat->matrix_sz; i++)
lpc32xx_mod_states(kscandat, i);
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
input_sync(kscandat->input);
return IRQ_HANDLED;
}
static int lpc32xx_kscan_open(struct input_dev *dev)
{
struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
int error;
error = clk_prepare_enable(kscandat->clk);
if (error)
return error;
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
return 0;
}
static void lpc32xx_kscan_close(struct input_dev *dev)
{
struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
clk_disable_unprepare(kscandat->clk);
}
static int lpc32xx_parse_dt(struct device *dev,
struct lpc32xx_kscan_drv *kscandat)
{
struct device_node *np = dev->of_node;
u32 rows = 0, columns = 0;
int err;
err = matrix_keypad_parse_of_params(dev, &rows, &columns);
if (err)
return err;
if (rows != columns) {
dev_err(dev, "rows and columns must be equal!\n");
return -EINVAL;
}
kscandat->matrix_sz = rows;
kscandat->row_shift = get_count_order(columns);
of_property_read_u32(np, "nxp,debounce-delay-ms", &kscandat->deb_clks);
of_property_read_u32(np, "nxp,scan-delay-ms", &kscandat->scan_delay);
if (!kscandat->deb_clks || !kscandat->scan_delay) {
dev_err(dev, "debounce or scan delay not specified\n");
return -EINVAL;
}
return 0;
}
static int lpc32xx_kscan_probe(struct platform_device *pdev)
{
struct lpc32xx_kscan_drv *kscandat;
struct input_dev *input;
struct resource *res;
size_t keymap_size;
int error;
int irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get platform I/O memory\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0 || irq >= NR_IRQS) {
dev_err(&pdev->dev, "failed to get platform irq\n");
return -EINVAL;
}
kscandat = kzalloc(sizeof(struct lpc32xx_kscan_drv), GFP_KERNEL);
if (!kscandat) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
error = lpc32xx_parse_dt(&pdev->dev, kscandat);
if (error) {
dev_err(&pdev->dev, "failed to parse device tree\n");
goto err_free_mem;
}
keymap_size = sizeof(kscandat->keymap[0]) *
(kscandat->matrix_sz << kscandat->row_shift);
kscandat->keymap = kzalloc(keymap_size, GFP_KERNEL);
if (!kscandat->keymap) {
dev_err(&pdev->dev, "could not allocate memory for keymap\n");
error = -ENOMEM;
goto err_free_mem;
}
kscandat->input = input = input_allocate_device();
if (!input) {
dev_err(&pdev->dev, "failed to allocate input device\n");
error = -ENOMEM;
goto err_free_keymap;
}
/* Setup key input */
input->name = pdev->name;
input->phys = "lpc32xx/input0";
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->open = lpc32xx_kscan_open;
input->close = lpc32xx_kscan_close;
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_MSC, MSC_SCAN);
error = matrix_keypad_build_keymap(NULL, NULL,
kscandat->matrix_sz,
kscandat->matrix_sz,
kscandat->keymap, kscandat->input);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_free_input;
}
input_set_drvdata(kscandat->input, kscandat);
kscandat->iores = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!kscandat->iores) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto err_free_input;
}
kscandat->kscan_base = ioremap(kscandat->iores->start,
resource_size(kscandat->iores));
if (!kscandat->kscan_base) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -EBUSY;
goto err_release_memregion;
}
/* Get the key scanner clock */
kscandat->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(kscandat->clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
error = PTR_ERR(kscandat->clk);
goto err_unmap;
}
/* Configure the key scanner */
error = clk_prepare_enable(kscandat->clk);
if (error)
goto err_clk_put;
writel(kscandat->deb_clks, LPC32XX_KS_DEB(kscandat->kscan_base));
writel(kscandat->scan_delay, LPC32XX_KS_SCAN_CTL(kscandat->kscan_base));
writel(LPC32XX_KSCAN_FTST_USE32K_CLK,
LPC32XX_KS_FAST_TST(kscandat->kscan_base));
writel(kscandat->matrix_sz,
LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base));
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
clk_disable_unprepare(kscandat->clk);
error = request_irq(irq, lpc32xx_kscan_irq, 0, pdev->name, kscandat);
if (error) {
dev_err(&pdev->dev, "failed to request irq\n");
goto err_clk_put;
}
error = input_register_device(kscandat->input);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
goto err_free_irq;
}
platform_set_drvdata(pdev, kscandat);
return 0;
err_free_irq:
free_irq(irq, kscandat);
err_clk_put:
clk_put(kscandat->clk);
err_unmap:
iounmap(kscandat->kscan_base);
err_release_memregion:
release_mem_region(kscandat->iores->start,
resource_size(kscandat->iores));
err_free_input:
input_free_device(kscandat->input);
err_free_keymap:
kfree(kscandat->keymap);
err_free_mem:
kfree(kscandat);
return error;
}
static int lpc32xx_kscan_remove(struct platform_device *pdev)
{
struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
free_irq(platform_get_irq(pdev, 0), kscandat);
clk_put(kscandat->clk);
iounmap(kscandat->kscan_base);
release_mem_region(kscandat->iores->start,
resource_size(kscandat->iores));
input_unregister_device(kscandat->input);
kfree(kscandat->keymap);
kfree(kscandat);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int lpc32xx_kscan_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
struct input_dev *input = kscandat->input;
mutex_lock(&input->mutex);
if (input->users) {
/* Clear IRQ and disable clock */
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
clk_disable_unprepare(kscandat->clk);
}
mutex_unlock(&input->mutex);
return 0;
}
static int lpc32xx_kscan_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
struct input_dev *input = kscandat->input;
int retval = 0;
mutex_lock(&input->mutex);
if (input->users) {
/* Enable clock and clear IRQ */
retval = clk_prepare_enable(kscandat->clk);
if (retval == 0)
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
}
mutex_unlock(&input->mutex);
return retval;
}
#endif
static SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend,
lpc32xx_kscan_resume);
static const struct of_device_id lpc32xx_kscan_match[] = {
{ .compatible = "nxp,lpc3220-key" },
{},
};
MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match);
static struct platform_driver lpc32xx_kscan_driver = {
.probe = lpc32xx_kscan_probe,
.remove = lpc32xx_kscan_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lpc32xx_kscan_pm_ops,
.of_match_table = of_match_ptr(lpc32xx_kscan_match),
}
};
module_platform_driver(lpc32xx_kscan_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");

View File

@@ -0,0 +1,260 @@
/*
* SEGA Dreamcast keyboard driver
* Based on drivers/usb/usbkbd.c
* Copyright (c) YAEGASHI Takeshi, 2001
* Porting to 2.6 Copyright (c) Adrian McMenamin, 2007 - 2009
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/maple.h>
/* Very simple mutex to ensure proper cleanup */
static DEFINE_MUTEX(maple_keyb_mutex);
#define NR_SCANCODES 256
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk");
MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
MODULE_LICENSE("GPL");
struct dc_kbd {
struct input_dev *dev;
unsigned short keycode[NR_SCANCODES];
unsigned char new[8];
unsigned char old[8];
};
static const unsigned short dc_kbd_keycode[NR_SCANCODES] = {
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B,
KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,
KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V,
KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
KEY_7, KEY_8, KEY_9, KEY_0, KEY_ENTER, KEY_ESC, KEY_BACKSPACE,
KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,
KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON,
KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH,
KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ,
KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP,
KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN,
KEY_UP, KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS,
KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5,
KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, KEY_102ND,
KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15,
KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22,
KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, KEY_STOP,
KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE,
KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN,
KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA,
KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
KEY_RESERVED, KEY_RESERVED, KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT,
KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT,
KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,
KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP,
KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_SCREENLOCK, KEY_REFRESH,
KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
};
static void dc_scan_kbd(struct dc_kbd *kbd)
{
struct input_dev *dev = kbd->dev;
void *ptr;
int code, keycode;
int i;
for (i = 0; i < 8; i++) {
code = i + 224;
keycode = kbd->keycode[code];
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, (kbd->new[0] >> i) & 1);
}
for (i = 2; i < 8; i++) {
ptr = memchr(kbd->new + 2, kbd->old[i], 6);
code = kbd->old[i];
if (code > 3 && ptr == NULL) {
keycode = kbd->keycode[code];
if (keycode) {
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 0);
} else
dev_dbg(&dev->dev,
"Unknown key (scancode %#x) released.",
code);
}
ptr = memchr(kbd->old + 2, kbd->new[i], 6);
code = kbd->new[i];
if (code > 3 && ptr) {
keycode = kbd->keycode[code];
if (keycode) {
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 1);
} else
dev_dbg(&dev->dev,
"Unknown key (scancode %#x) pressed.",
code);
}
}
input_sync(dev);
memcpy(kbd->old, kbd->new, 8);
}
static void dc_kbd_callback(struct mapleq *mq)
{
struct maple_device *mapledev = mq->dev;
struct dc_kbd *kbd = maple_get_drvdata(mapledev);
unsigned long *buf = (unsigned long *)(mq->recvbuf->buf);
/*
* We should always get the lock because the only
* time it may be locked is if the driver is in the cleanup phase.
*/
if (likely(mutex_trylock(&maple_keyb_mutex))) {
if (buf[1] == mapledev->function) {
memcpy(kbd->new, buf + 2, 8);
dc_scan_kbd(kbd);
}
mutex_unlock(&maple_keyb_mutex);
}
}
static int probe_maple_kbd(struct device *dev)
{
struct maple_device *mdev;
struct maple_driver *mdrv;
int i, error;
struct dc_kbd *kbd;
struct input_dev *idev;
mdev = to_maple_dev(dev);
mdrv = to_maple_driver(dev->driver);
kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
if (!kbd) {
error = -ENOMEM;
goto fail;
}
idev = input_allocate_device();
if (!idev) {
error = -ENOMEM;
goto fail_idev_alloc;
}
kbd->dev = idev;
memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
idev->name = mdev->product_name;
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
idev->keycode = kbd->keycode;
idev->keycodesize = sizeof(unsigned short);
idev->keycodemax = ARRAY_SIZE(kbd->keycode);
idev->id.bustype = BUS_HOST;
idev->dev.parent = &mdev->dev;
for (i = 0; i < NR_SCANCODES; i++)
__set_bit(dc_kbd_keycode[i], idev->keybit);
__clear_bit(KEY_RESERVED, idev->keybit);
input_set_capability(idev, EV_MSC, MSC_SCAN);
input_set_drvdata(idev, kbd);
error = input_register_device(idev);
if (error)
goto fail_register;
/* Maple polling is locked to VBLANK - which may be just 50/s */
maple_getcond_callback(mdev, dc_kbd_callback, HZ/50,
MAPLE_FUNC_KEYBOARD);
mdev->driver = mdrv;
maple_set_drvdata(mdev, kbd);
return error;
fail_register:
maple_set_drvdata(mdev, NULL);
input_free_device(idev);
fail_idev_alloc:
kfree(kbd);
fail:
return error;
}
static int remove_maple_kbd(struct device *dev)
{
struct maple_device *mdev = to_maple_dev(dev);
struct dc_kbd *kbd = maple_get_drvdata(mdev);
mutex_lock(&maple_keyb_mutex);
input_unregister_device(kbd->dev);
kfree(kbd);
maple_set_drvdata(mdev, NULL);
mutex_unlock(&maple_keyb_mutex);
return 0;
}
static struct maple_driver dc_kbd_driver = {
.function = MAPLE_FUNC_KEYBOARD,
.drv = {
.name = "Dreamcast_keyboard",
.probe = probe_maple_kbd,
.remove = remove_maple_kbd,
},
};
static int __init dc_kbd_init(void)
{
return maple_driver_register(&dc_kbd_driver);
}
static void __exit dc_kbd_exit(void)
{
maple_driver_unregister(&dc_kbd_driver);
}
module_init(dc_kbd_init);
module_exit(dc_kbd_exit);

View File

@@ -0,0 +1,580 @@
/*
* GPIO driven matrix keyboard driver
*
* Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com>
*
* Based on corgikbd.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
struct matrix_keypad {
const struct matrix_keypad_platform_data *pdata;
struct input_dev *input_dev;
unsigned int row_shift;
DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
uint32_t last_key_state[MATRIX_MAX_COLS];
struct delayed_work work;
spinlock_t lock;
bool scan_pending;
bool stopped;
bool gpio_all_disabled;
};
/*
* NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
* minmal side effect when scanning other columns, here it is configured to
* be input, and it should work on most platforms.
*/
static void __activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
{
bool level_on = !pdata->active_low;
if (on) {
gpio_direction_output(pdata->col_gpios[col], level_on);
} else {
gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
gpio_direction_input(pdata->col_gpios[col]);
}
}
static void activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
{
__activate_col(pdata, col, on);
if (on && pdata->col_scan_delay_us)
udelay(pdata->col_scan_delay_us);
}
static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
bool on)
{
int col;
for (col = 0; col < pdata->num_col_gpios; col++)
__activate_col(pdata, col, on);
}
static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
int row)
{
return gpio_get_value_cansleep(pdata->row_gpios[row]) ?
!pdata->active_low : pdata->active_low;
}
static void enable_row_irqs(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;
if (pdata->clustered_irq > 0)
enable_irq(pdata->clustered_irq);
else {
for (i = 0; i < pdata->num_row_gpios; i++)
enable_irq(gpio_to_irq(pdata->row_gpios[i]));
}
}
static void disable_row_irqs(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;
if (pdata->clustered_irq > 0)
disable_irq_nosync(pdata->clustered_irq);
else {
for (i = 0; i < pdata->num_row_gpios; i++)
disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
}
}
/*
* This gets the keys from keyboard and reports it to input subsystem
*/
static void matrix_keypad_scan(struct work_struct *work)
{
struct matrix_keypad *keypad =
container_of(work, struct matrix_keypad, work.work);
struct input_dev *input_dev = keypad->input_dev;
const unsigned short *keycodes = input_dev->keycode;
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
uint32_t new_state[MATRIX_MAX_COLS];
int row, col, code;
/* de-activate all columns for scanning */
activate_all_cols(pdata, false);
memset(new_state, 0, sizeof(new_state));
/* assert each column and read the row status out */
for (col = 0; col < pdata->num_col_gpios; col++) {
activate_col(pdata, col, true);
for (row = 0; row < pdata->num_row_gpios; row++)
new_state[col] |=
row_asserted(pdata, row) ? (1 << row) : 0;
activate_col(pdata, col, false);
}
for (col = 0; col < pdata->num_col_gpios; col++) {
uint32_t bits_changed;
bits_changed = keypad->last_key_state[col] ^ new_state[col];
if (bits_changed == 0)
continue;
for (row = 0; row < pdata->num_row_gpios; row++) {
if ((bits_changed & (1 << row)) == 0)
continue;
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev,
keycodes[code],
new_state[col] & (1 << row));
}
}
input_sync(input_dev);
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
activate_all_cols(pdata, true);
/* Enable IRQs again */
spin_lock_irq(&keypad->lock);
keypad->scan_pending = false;
enable_row_irqs(keypad);
spin_unlock_irq(&keypad->lock);
}
static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
{
struct matrix_keypad *keypad = id;
unsigned long flags;
spin_lock_irqsave(&keypad->lock, flags);
/*
* See if another IRQ beaten us to it and scheduled the
* scan already. In that case we should not try to
* disable IRQs again.
*/
if (unlikely(keypad->scan_pending || keypad->stopped))
goto out;
disable_row_irqs(keypad);
keypad->scan_pending = true;
schedule_delayed_work(&keypad->work,
msecs_to_jiffies(keypad->pdata->debounce_ms));
out:
spin_unlock_irqrestore(&keypad->lock, flags);
return IRQ_HANDLED;
}
static int matrix_keypad_start(struct input_dev *dev)
{
struct matrix_keypad *keypad = input_get_drvdata(dev);
keypad->stopped = false;
mb();
/*
* Schedule an immediate key scan to capture current key state;
* columns will be activated and IRQs be enabled after the scan.
*/
schedule_delayed_work(&keypad->work, 0);
return 0;
}
static void matrix_keypad_stop(struct input_dev *dev)
{
struct matrix_keypad *keypad = input_get_drvdata(dev);
keypad->stopped = true;
mb();
flush_work(&keypad->work.work);
/*
* matrix_keypad_scan() will leave IRQs enabled;
* we should disable them now.
*/
disable_row_irqs(keypad);
}
#ifdef CONFIG_PM_SLEEP
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
unsigned int gpio;
int i;
if (pdata->clustered_irq > 0) {
if (enable_irq_wake(pdata->clustered_irq) == 0)
keypad->gpio_all_disabled = true;
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
if (!test_bit(i, keypad->disabled_gpios)) {
gpio = pdata->row_gpios[i];
if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
__set_bit(i, keypad->disabled_gpios);
}
}
}
}
static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
unsigned int gpio;
int i;
if (pdata->clustered_irq > 0) {
if (keypad->gpio_all_disabled) {
disable_irq_wake(pdata->clustered_irq);
keypad->gpio_all_disabled = false;
}
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
if (test_and_clear_bit(i, keypad->disabled_gpios)) {
gpio = pdata->row_gpios[i];
disable_irq_wake(gpio_to_irq(gpio));
}
}
}
}
static int matrix_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
matrix_keypad_stop(keypad->input_dev);
if (device_may_wakeup(&pdev->dev))
matrix_keypad_enable_wakeup(keypad);
return 0;
}
static int matrix_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
if (device_may_wakeup(&pdev->dev))
matrix_keypad_disable_wakeup(keypad);
matrix_keypad_start(keypad->input_dev);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
matrix_keypad_suspend, matrix_keypad_resume);
static int matrix_keypad_init_gpio(struct platform_device *pdev,
struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i, err;
/* initialized strobe lines as outputs, activated */
for (i = 0; i < pdata->num_col_gpios; i++) {
err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col");
if (err) {
dev_err(&pdev->dev,
"failed to request GPIO%d for COL%d\n",
pdata->col_gpios[i], i);
goto err_free_cols;
}
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
}
for (i = 0; i < pdata->num_row_gpios; i++) {
err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
if (err) {
dev_err(&pdev->dev,
"failed to request GPIO%d for ROW%d\n",
pdata->row_gpios[i], i);
goto err_free_rows;
}
gpio_direction_input(pdata->row_gpios[i]);
}
if (pdata->clustered_irq > 0) {
err = request_irq(pdata->clustered_irq,
matrix_keypad_interrupt,
pdata->clustered_irq_flags,
"matrix-keypad", keypad);
if (err) {
dev_err(&pdev->dev,
"Unable to acquire clustered interrupt\n");
goto err_free_rows;
}
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
matrix_keypad_interrupt,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad);
if (err) {
dev_err(&pdev->dev,
"Unable to acquire interrupt for GPIO line %i\n",
pdata->row_gpios[i]);
goto err_free_irqs;
}
}
}
/* initialized as disabled - enabled by input->open */
disable_row_irqs(keypad);
return 0;
err_free_irqs:
while (--i >= 0)
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
i = pdata->num_row_gpios;
err_free_rows:
while (--i >= 0)
gpio_free(pdata->row_gpios[i]);
i = pdata->num_col_gpios;
err_free_cols:
while (--i >= 0)
gpio_free(pdata->col_gpios[i]);
return err;
}
static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;
if (pdata->clustered_irq > 0) {
free_irq(pdata->clustered_irq, keypad);
} else {
for (i = 0; i < pdata->num_row_gpios; i++)
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
}
for (i = 0; i < pdata->num_row_gpios; i++)
gpio_free(pdata->row_gpios[i]);
for (i = 0; i < pdata->num_col_gpios; i++)
gpio_free(pdata->col_gpios[i]);
}
#ifdef CONFIG_OF
static struct matrix_keypad_platform_data *
matrix_keypad_parse_dt(struct device *dev)
{
struct matrix_keypad_platform_data *pdata;
struct device_node *np = dev->of_node;
unsigned int *gpios;
int i, nrow, ncol;
if (!np) {
dev_err(dev, "device lacks DT data\n");
return ERR_PTR(-ENODEV);
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for platform data\n");
return ERR_PTR(-ENOMEM);
}
pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
if (nrow <= 0 || ncol <= 0) {
dev_err(dev, "number of keypad rows/columns not specified\n");
return ERR_PTR(-EINVAL);
}
if (of_get_property(np, "linux,no-autorepeat", NULL))
pdata->no_autorepeat = true;
if (of_get_property(np, "linux,wakeup", NULL))
pdata->wakeup = true;
if (of_get_property(np, "gpio-activelow", NULL))
pdata->active_low = true;
of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
of_property_read_u32(np, "col-scan-delay-us",
&pdata->col_scan_delay_us);
gpios = devm_kzalloc(dev,
sizeof(unsigned int) *
(pdata->num_row_gpios + pdata->num_col_gpios),
GFP_KERNEL);
if (!gpios) {
dev_err(dev, "could not allocate memory for gpios\n");
return ERR_PTR(-ENOMEM);
}
for (i = 0; i < pdata->num_row_gpios; i++)
gpios[i] = of_get_named_gpio(np, "row-gpios", i);
for (i = 0; i < pdata->num_col_gpios; i++)
gpios[pdata->num_row_gpios + i] =
of_get_named_gpio(np, "col-gpios", i);
pdata->row_gpios = gpios;
pdata->col_gpios = &gpios[pdata->num_row_gpios];
return pdata;
}
#else
static inline struct matrix_keypad_platform_data *
matrix_keypad_parse_dt(struct device *dev)
{
dev_err(dev, "no platform data defined\n");
return ERR_PTR(-EINVAL);
}
#endif
static int matrix_keypad_probe(struct platform_device *pdev)
{
const struct matrix_keypad_platform_data *pdata;
struct matrix_keypad *keypad;
struct input_dev *input_dev;
int err;
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
pdata = matrix_keypad_parse_dt(&pdev->dev);
if (IS_ERR(pdata)) {
dev_err(&pdev->dev, "no platform data defined\n");
return PTR_ERR(pdata);
}
} else if (!pdata->keymap_data) {
dev_err(&pdev->dev, "no keymap data defined\n");
return -EINVAL;
}
keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
input_dev = input_allocate_device();
if (!keypad || !input_dev) {
err = -ENOMEM;
goto err_free_mem;
}
keypad->input_dev = input_dev;
keypad->pdata = pdata;
keypad->row_shift = get_count_order(pdata->num_col_gpios);
keypad->stopped = true;
INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
spin_lock_init(&keypad->lock);
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
input_dev->open = matrix_keypad_start;
input_dev->close = matrix_keypad_stop;
err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
pdata->num_row_gpios,
pdata->num_col_gpios,
NULL, input_dev);
if (err) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_free_mem;
}
if (!pdata->no_autorepeat)
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
err = matrix_keypad_init_gpio(pdev, keypad);
if (err)
goto err_free_mem;
err = input_register_device(keypad->input_dev);
if (err)
goto err_free_gpio;
device_init_wakeup(&pdev->dev, pdata->wakeup);
platform_set_drvdata(pdev, keypad);
return 0;
err_free_gpio:
matrix_keypad_free_gpio(keypad);
err_free_mem:
input_free_device(input_dev);
kfree(keypad);
return err;
}
static int matrix_keypad_remove(struct platform_device *pdev)
{
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
matrix_keypad_free_gpio(keypad);
input_unregister_device(keypad->input_dev);
kfree(keypad);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id matrix_keypad_dt_match[] = {
{ .compatible = "gpio-matrix-keypad" },
{ }
};
MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
#endif
static struct platform_driver matrix_keypad_driver = {
.probe = matrix_keypad_probe,
.remove = matrix_keypad_remove,
.driver = {
.name = "matrix-keypad",
.owner = THIS_MODULE,
.pm = &matrix_keypad_pm_ops,
.of_match_table = of_match_ptr(matrix_keypad_dt_match),
},
};
module_platform_driver(matrix_keypad_driver);
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:matrix-keypad");

View File

@@ -0,0 +1,323 @@
/*
* max7359_keypad.c - MAX7359 Key Switch Controller Driver
*
* Copyright (C) 2009 Samsung Electronics
* Kim Kyuwon <q1.kim@samsung.com>
*
* Based on pxa27x_keypad.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/5456
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#define MAX7359_MAX_KEY_ROWS 8
#define MAX7359_MAX_KEY_COLS 8
#define MAX7359_MAX_KEY_NUM (MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS)
#define MAX7359_ROW_SHIFT 3
/*
* MAX7359 registers
*/
#define MAX7359_REG_KEYFIFO 0x00
#define MAX7359_REG_CONFIG 0x01
#define MAX7359_REG_DEBOUNCE 0x02
#define MAX7359_REG_INTERRUPT 0x03
#define MAX7359_REG_PORTS 0x04
#define MAX7359_REG_KEYREP 0x05
#define MAX7359_REG_SLEEP 0x06
/*
* Configuration register bits
*/
#define MAX7359_CFG_SLEEP (1 << 7)
#define MAX7359_CFG_INTERRUPT (1 << 5)
#define MAX7359_CFG_KEY_RELEASE (1 << 3)
#define MAX7359_CFG_WAKEUP (1 << 1)
#define MAX7359_CFG_TIMEOUT (1 << 0)
/*
* Autosleep register values (ms)
*/
#define MAX7359_AUTOSLEEP_8192 0x01
#define MAX7359_AUTOSLEEP_4096 0x02
#define MAX7359_AUTOSLEEP_2048 0x03
#define MAX7359_AUTOSLEEP_1024 0x04
#define MAX7359_AUTOSLEEP_512 0x05
#define MAX7359_AUTOSLEEP_256 0x06
struct max7359_keypad {
/* matrix key code map */
unsigned short keycodes[MAX7359_MAX_KEY_NUM];
struct input_dev *input_dev;
struct i2c_client *client;
};
static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val)
{
int ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0)
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
__func__, reg, val, ret);
return ret;
}
static int max7359_read_reg(struct i2c_client *client, int reg)
{
int ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
__func__, reg, ret);
return ret;
}
static void max7359_build_keycode(struct max7359_keypad *keypad,
const struct matrix_keymap_data *keymap_data)
{
struct input_dev *input_dev = keypad->input_dev;
int i;
for (i = 0; i < keymap_data->keymap_size; i++) {
unsigned int key = keymap_data->keymap[i];
unsigned int row = KEY_ROW(key);
unsigned int col = KEY_COL(key);
unsigned int scancode = MATRIX_SCAN_CODE(row, col,
MAX7359_ROW_SHIFT);
unsigned short keycode = KEY_VAL(key);
keypad->keycodes[scancode] = keycode;
__set_bit(keycode, input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
}
/* runs in an IRQ thread -- can (and will!) sleep */
static irqreturn_t max7359_interrupt(int irq, void *dev_id)
{
struct max7359_keypad *keypad = dev_id;
struct input_dev *input_dev = keypad->input_dev;
int val, row, col, release, code;
val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO);
row = val & 0x7;
col = (val >> 3) & 0x7;
release = val & 0x40;
code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT);
dev_dbg(&keypad->client->dev,
"key[%d:%d] %s\n", row, col, release ? "release" : "press");
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, keypad->keycodes[code], !release);
input_sync(input_dev);
return IRQ_HANDLED;
}
/*
* Let MAX7359 fall into a deep sleep:
* If no keys are pressed, enter sleep mode for 8192 ms. And if any
* key is pressed, the MAX7359 returns to normal operating mode.
*/
static inline void max7359_fall_deepsleep(struct i2c_client *client)
{
max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192);
}
/*
* Let MAX7359 take a catnap:
* Autosleep just for 256 ms.
*/
static inline void max7359_take_catnap(struct i2c_client *client)
{
max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256);
}
static int max7359_open(struct input_dev *dev)
{
struct max7359_keypad *keypad = input_get_drvdata(dev);
max7359_take_catnap(keypad->client);
return 0;
}
static void max7359_close(struct input_dev *dev)
{
struct max7359_keypad *keypad = input_get_drvdata(dev);
max7359_fall_deepsleep(keypad->client);
}
static void max7359_initialize(struct i2c_client *client)
{
max7359_write_reg(client, MAX7359_REG_CONFIG,
MAX7359_CFG_INTERRUPT | /* Irq clears after host read */
MAX7359_CFG_KEY_RELEASE | /* Key release enable */
MAX7359_CFG_WAKEUP); /* Key press wakeup enable */
/* Full key-scan functionality */
max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F);
/* nINT asserts every debounce cycles */
max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01);
max7359_fall_deepsleep(client);
}
static int max7359_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct matrix_keymap_data *keymap_data = client->dev.platform_data;
struct max7359_keypad *keypad;
struct input_dev *input_dev;
int ret;
int error;
if (!client->irq) {
dev_err(&client->dev, "The irq number should not be zero\n");
return -EINVAL;
}
/* Detect MAX7359: The initial Keys FIFO value is '0x3F' */
ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO);
if (ret < 0) {
dev_err(&client->dev, "failed to detect device\n");
return -ENODEV;
}
dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret);
keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL);
input_dev = input_allocate_device();
if (!keypad || !input_dev) {
dev_err(&client->dev, "failed to allocate memory\n");
error = -ENOMEM;
goto failed_free_mem;
}
keypad->client = client;
keypad->input_dev = input_dev;
input_dev->name = client->name;
input_dev->id.bustype = BUS_I2C;
input_dev->open = max7359_open;
input_dev->close = max7359_close;
input_dev->dev.parent = &client->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_dev->keycodesize = sizeof(keypad->keycodes[0]);
input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
input_dev->keycode = keypad->keycodes;
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
max7359_build_keycode(keypad, keymap_data);
error = request_threaded_irq(client->irq, NULL, max7359_interrupt,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
client->name, keypad);
if (error) {
dev_err(&client->dev, "failed to register interrupt\n");
goto failed_free_mem;
}
/* Register the input device */
error = input_register_device(input_dev);
if (error) {
dev_err(&client->dev, "failed to register input device\n");
goto failed_free_irq;
}
/* Initialize MAX7359 */
max7359_initialize(client);
i2c_set_clientdata(client, keypad);
device_init_wakeup(&client->dev, 1);
return 0;
failed_free_irq:
free_irq(client->irq, keypad);
failed_free_mem:
input_free_device(input_dev);
kfree(keypad);
return error;
}
static int max7359_remove(struct i2c_client *client)
{
struct max7359_keypad *keypad = i2c_get_clientdata(client);
free_irq(client->irq, keypad);
input_unregister_device(keypad->input_dev);
kfree(keypad);
return 0;
}
#ifdef CONFIG_PM
static int max7359_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
max7359_fall_deepsleep(client);
if (device_may_wakeup(&client->dev))
enable_irq_wake(client->irq);
return 0;
}
static int max7359_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (device_may_wakeup(&client->dev))
disable_irq_wake(client->irq);
/* Restore the default setting */
max7359_take_catnap(client);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume);
static const struct i2c_device_id max7359_ids[] = {
{ "max7359", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max7359_ids);
static struct i2c_driver max7359_i2c_driver = {
.driver = {
.name = "max7359",
.pm = &max7359_pm,
},
.probe = max7359_probe,
.remove = max7359_remove,
.id_table = max7359_ids,
};
module_i2c_driver(max7359_i2c_driver);
MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,284 @@
/*
* Touchkey driver for MELFAS MCS5000/5080 controller
*
* Copyright (C) 2010 Samsung Electronics Co.Ltd
* Author: HeungJun Kim <riverful.kim@samsung.com>
* Author: Joonyoung Shim <jy0922.shim@samsung.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c/mcs.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/pm.h>
/* MCS5000 Touchkey */
#define MCS5000_TOUCHKEY_STATUS 0x04
#define MCS5000_TOUCHKEY_STATUS_PRESS 7
#define MCS5000_TOUCHKEY_FW 0x0a
#define MCS5000_TOUCHKEY_BASE_VAL 0x61
/* MCS5080 Touchkey */
#define MCS5080_TOUCHKEY_STATUS 0x00
#define MCS5080_TOUCHKEY_STATUS_PRESS 3
#define MCS5080_TOUCHKEY_FW 0x01
#define MCS5080_TOUCHKEY_BASE_VAL 0x1
enum mcs_touchkey_type {
MCS5000_TOUCHKEY,
MCS5080_TOUCHKEY,
};
struct mcs_touchkey_chip {
unsigned int status_reg;
unsigned int pressbit;
unsigned int press_invert;
unsigned int baseval;
};
struct mcs_touchkey_data {
void (*poweron)(bool);
struct i2c_client *client;
struct input_dev *input_dev;
struct mcs_touchkey_chip chip;
unsigned int key_code;
unsigned int key_val;
unsigned short keycodes[];
};
static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
{
struct mcs_touchkey_data *data = dev_id;
struct mcs_touchkey_chip *chip = &data->chip;
struct i2c_client *client = data->client;
struct input_dev *input = data->input_dev;
unsigned int key_val;
unsigned int pressed;
int val;
val = i2c_smbus_read_byte_data(client, chip->status_reg);
if (val < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", val);
goto out;
}
pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
if (chip->press_invert)
pressed ^= chip->press_invert;
/* key_val is 0 when released, so we should use key_val of press. */
if (pressed) {
key_val = val & (0xff >> (8 - chip->pressbit));
if (!key_val)
goto out;
key_val -= chip->baseval;
data->key_code = data->keycodes[key_val];
data->key_val = key_val;
}
input_event(input, EV_MSC, MSC_SCAN, data->key_val);
input_report_key(input, data->key_code, pressed);
input_sync(input);
dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
pressed ? "pressed" : "released");
out:
return IRQ_HANDLED;
}
static int mcs_touchkey_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct mcs_platform_data *pdata;
struct mcs_touchkey_data *data;
struct input_dev *input_dev;
unsigned int fw_reg;
int fw_ver;
int error;
int i;
pdata = client->dev.platform_data;
if (!pdata) {
dev_err(&client->dev, "no platform data defined\n");
return -EINVAL;
}
data = kzalloc(sizeof(struct mcs_touchkey_data) +
sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
GFP_KERNEL);
input_dev = input_allocate_device();
if (!data || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n");
error = -ENOMEM;
goto err_free_mem;
}
data->client = client;
data->input_dev = input_dev;
if (id->driver_data == MCS5000_TOUCHKEY) {
data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
fw_reg = MCS5000_TOUCHKEY_FW;
} else {
data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
data->chip.press_invert = 1;
data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
fw_reg = MCS5080_TOUCHKEY_FW;
}
fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
if (fw_ver < 0) {
error = fw_ver;
dev_err(&client->dev, "i2c read error[%d]\n", error);
goto err_free_mem;
}
dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
input_dev->name = "MELPAS MCS Touchkey";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY);
if (!pdata->no_autorepeat)
input_dev->evbit[0] |= BIT_MASK(EV_REP);
input_dev->keycode = data->keycodes;
input_dev->keycodesize = sizeof(data->keycodes[0]);
input_dev->keycodemax = pdata->key_maxval + 1;
for (i = 0; i < pdata->keymap_size; i++) {
unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
data->keycodes[val] = code;
__set_bit(code, input_dev->keybit);
}
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, data);
if (pdata->cfg_pin)
pdata->cfg_pin();
if (pdata->poweron) {
data->poweron = pdata->poweron;
data->poweron(true);
}
error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->dev.driver->name, data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
goto err_free_mem;
}
error = input_register_device(input_dev);
if (error)
goto err_free_irq;
i2c_set_clientdata(client, data);
return 0;
err_free_irq:
free_irq(client->irq, data);
err_free_mem:
input_free_device(input_dev);
kfree(data);
return error;
}
static int mcs_touchkey_remove(struct i2c_client *client)
{
struct mcs_touchkey_data *data = i2c_get_clientdata(client);
free_irq(client->irq, data);
if (data->poweron)
data->poweron(false);
input_unregister_device(data->input_dev);
kfree(data);
return 0;
}
static void mcs_touchkey_shutdown(struct i2c_client *client)
{
struct mcs_touchkey_data *data = i2c_get_clientdata(client);
if (data->poweron)
data->poweron(false);
}
#ifdef CONFIG_PM_SLEEP
static int mcs_touchkey_suspend(struct device *dev)
{
struct mcs_touchkey_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
/* Disable the work */
disable_irq(client->irq);
/* Finally turn off the power */
if (data->poweron)
data->poweron(false);
return 0;
}
static int mcs_touchkey_resume(struct device *dev)
{
struct mcs_touchkey_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
/* Enable the device first */
if (data->poweron)
data->poweron(true);
/* Enable irq again */
enable_irq(client->irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
mcs_touchkey_suspend, mcs_touchkey_resume);
static const struct i2c_device_id mcs_touchkey_id[] = {
{ "mcs5000_touchkey", MCS5000_TOUCHKEY },
{ "mcs5080_touchkey", MCS5080_TOUCHKEY },
{ }
};
MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
static struct i2c_driver mcs_touchkey_driver = {
.driver = {
.name = "mcs_touchkey",
.owner = THIS_MODULE,
.pm = &mcs_touchkey_pm_ops,
},
.probe = mcs_touchkey_probe,
.remove = mcs_touchkey_remove,
.shutdown = mcs_touchkey_shutdown,
.id_table = mcs_touchkey_id,
};
module_i2c_driver(mcs_touchkey_driver);
/* Module information */
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,337 @@
/*
* Touchkey driver for Freescale MPR121 Controllor
*
* Copyright (C) 2011 Freescale Semiconductor, Inc.
* Author: Zhang Jiejing <jiejing.zhang@freescale.com>
*
* Based on mcs_touchkey.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/i2c/mpr121_touchkey.h>
/* Register definitions */
#define ELE_TOUCH_STATUS_0_ADDR 0x0
#define ELE_TOUCH_STATUS_1_ADDR 0X1
#define MHD_RISING_ADDR 0x2b
#define NHD_RISING_ADDR 0x2c
#define NCL_RISING_ADDR 0x2d
#define FDL_RISING_ADDR 0x2e
#define MHD_FALLING_ADDR 0x2f
#define NHD_FALLING_ADDR 0x30
#define NCL_FALLING_ADDR 0x31
#define FDL_FALLING_ADDR 0x32
#define ELE0_TOUCH_THRESHOLD_ADDR 0x41
#define ELE0_RELEASE_THRESHOLD_ADDR 0x42
#define AFE_CONF_ADDR 0x5c
#define FILTER_CONF_ADDR 0x5d
/*
* ELECTRODE_CONF_ADDR: This register configures the number of
* enabled capacitance sensing inputs and its run/suspend mode.
*/
#define ELECTRODE_CONF_ADDR 0x5e
#define ELECTRODE_CONF_QUICK_CHARGE 0x80
#define AUTO_CONFIG_CTRL_ADDR 0x7b
#define AUTO_CONFIG_USL_ADDR 0x7d
#define AUTO_CONFIG_LSL_ADDR 0x7e
#define AUTO_CONFIG_TL_ADDR 0x7f
/* Threshold of touch/release trigger */
#define TOUCH_THRESHOLD 0x08
#define RELEASE_THRESHOLD 0x05
/* Masks for touch and release triggers */
#define TOUCH_STATUS_MASK 0xfff
/* MPR121 has 12 keys */
#define MPR121_MAX_KEY_COUNT 12
struct mpr121_touchkey {
struct i2c_client *client;
struct input_dev *input_dev;
unsigned int key_val;
unsigned int statusbits;
unsigned int keycount;
u16 keycodes[MPR121_MAX_KEY_COUNT];
};
struct mpr121_init_register {
int addr;
u8 val;
};
static const struct mpr121_init_register init_reg_table[] = {
{ MHD_RISING_ADDR, 0x1 },
{ NHD_RISING_ADDR, 0x1 },
{ MHD_FALLING_ADDR, 0x1 },
{ NHD_FALLING_ADDR, 0x1 },
{ NCL_FALLING_ADDR, 0xff },
{ FDL_FALLING_ADDR, 0x02 },
{ FILTER_CONF_ADDR, 0x04 },
{ AFE_CONF_ADDR, 0x0b },
{ AUTO_CONFIG_CTRL_ADDR, 0x0b },
};
static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
{
struct mpr121_touchkey *mpr121 = dev_id;
struct i2c_client *client = mpr121->client;
struct input_dev *input = mpr121->input_dev;
unsigned int key_num, key_val, pressed;
int reg;
reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
if (reg < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", reg);
goto out;
}
reg <<= 8;
reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
if (reg < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", reg);
goto out;
}
reg &= TOUCH_STATUS_MASK;
/* use old press bit to figure out which bit changed */
key_num = ffs(reg ^ mpr121->statusbits) - 1;
pressed = reg & (1 << key_num);
mpr121->statusbits = reg;
key_val = mpr121->keycodes[key_num];
input_event(input, EV_MSC, MSC_SCAN, key_num);
input_report_key(input, key_val, pressed);
input_sync(input);
dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val,
pressed ? "pressed" : "released");
out:
return IRQ_HANDLED;
}
static int mpr121_phys_init(const struct mpr121_platform_data *pdata,
struct mpr121_touchkey *mpr121,
struct i2c_client *client)
{
const struct mpr121_init_register *reg;
unsigned char usl, lsl, tl, eleconf;
int i, t, vdd, ret;
/* Set up touch/release threshold for ele0-ele11 */
for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) {
t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2);
ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD);
if (ret < 0)
goto err_i2c_write;
ret = i2c_smbus_write_byte_data(client, t + 1,
RELEASE_THRESHOLD);
if (ret < 0)
goto err_i2c_write;
}
/* Set up init register */
for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) {
reg = &init_reg_table[i];
ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val);
if (ret < 0)
goto err_i2c_write;
}
/*
* Capacitance on sensing input varies and needs to be compensated.
* The internal MPR121-auto-configuration can do this if it's
* registers are set properly (based on pdata->vdd_uv).
*/
vdd = pdata->vdd_uv / 1000;
usl = ((vdd - 700) * 256) / vdd;
lsl = (usl * 65) / 100;
tl = (usl * 90) / 100;
ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
/*
* Quick charge bit will let the capacitive charge to ready
* state quickly, or the buttons may not function after system
* boot.
*/
eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE;
ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
eleconf);
if (ret != 0)
goto err_i2c_write;
dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount);
return 0;
err_i2c_write:
dev_err(&client->dev, "i2c write error: %d\n", ret);
return ret;
}
static int mpr_touchkey_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct mpr121_platform_data *pdata = client->dev.platform_data;
struct mpr121_touchkey *mpr121;
struct input_dev *input_dev;
int error;
int i;
if (!pdata) {
dev_err(&client->dev, "no platform data defined\n");
return -EINVAL;
}
if (!pdata->keymap || !pdata->keymap_size) {
dev_err(&client->dev, "missing keymap data\n");
return -EINVAL;
}
if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) {
dev_err(&client->dev, "too many keys defined\n");
return -EINVAL;
}
if (!client->irq) {
dev_err(&client->dev, "irq number should not be zero\n");
return -EINVAL;
}
mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mpr121 || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n");
error = -ENOMEM;
goto err_free_mem;
}
mpr121->client = client;
mpr121->input_dev = input_dev;
mpr121->keycount = pdata->keymap_size;
input_dev->name = "Freescale MPR121 Touchkey";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_dev->keycode = mpr121->keycodes;
input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
input_dev->keycodemax = mpr121->keycount;
for (i = 0; i < pdata->keymap_size; i++) {
input_set_capability(input_dev, EV_KEY, pdata->keymap[i]);
mpr121->keycodes[i] = pdata->keymap[i];
}
error = mpr121_phys_init(pdata, mpr121, client);
if (error) {
dev_err(&client->dev, "Failed to init register\n");
goto err_free_mem;
}
error = request_threaded_irq(client->irq, NULL,
mpr_touchkey_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->dev.driver->name, mpr121);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
goto err_free_mem;
}
error = input_register_device(input_dev);
if (error)
goto err_free_irq;
i2c_set_clientdata(client, mpr121);
device_init_wakeup(&client->dev, pdata->wakeup);
return 0;
err_free_irq:
free_irq(client->irq, mpr121);
err_free_mem:
input_free_device(input_dev);
kfree(mpr121);
return error;
}
static int mpr_touchkey_remove(struct i2c_client *client)
{
struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
free_irq(client->irq, mpr121);
input_unregister_device(mpr121->input_dev);
kfree(mpr121);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int mpr_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (device_may_wakeup(&client->dev))
enable_irq_wake(client->irq);
i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00);
return 0;
}
static int mpr_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
disable_irq_wake(client->irq);
i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
mpr121->keycount);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume);
static const struct i2c_device_id mpr121_id[] = {
{ "mpr121_touchkey", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mpr121_id);
static struct i2c_driver mpr_touchkey_driver = {
.driver = {
.name = "mpr121",
.owner = THIS_MODULE,
.pm = &mpr121_touchkey_pm_ops,
},
.id_table = mpr121_id,
.probe = mpr_touchkey_probe,
.remove = mpr_touchkey_remove,
};
module_i2c_driver(mpr_touchkey_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip");

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2000 Justin Cormack
*/
/*
* Newton keyboard driver for Linux
*/
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail:
* Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#define DRIVER_DESC "Newton keyboard driver"
MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define NKBD_KEY 0x7f
#define NKBD_PRESS 0x80
static unsigned char nkbd_keycode[128] = {
KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
};
struct nkbd {
unsigned char keycode[128];
struct input_dev *dev;
struct serio *serio;
char phys[32];
};
static irqreturn_t nkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct nkbd *nkbd = serio_get_drvdata(serio);
/* invalid scan codes are probably the init sequence, so we ignore them */
if (nkbd->keycode[data & NKBD_KEY]) {
input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
input_sync(nkbd->dev);
}
else if (data == 0xe7) /* end of init sequence */
printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys);
return IRQ_HANDLED;
}
static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct nkbd *nkbd;
struct input_dev *input_dev;
int err = -ENOMEM;
int i;
nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!nkbd || !input_dev)
goto fail1;
nkbd->serio = serio;
nkbd->dev = input_dev;
snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys);
memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
input_dev->name = "Newton Keyboard";
input_dev->phys = nkbd->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_NEWTON;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_dev->keycode = nkbd->keycode;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode);
for (i = 0; i < 128; i++)
set_bit(nkbd->keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
serio_set_drvdata(serio, nkbd);
err = serio_open(serio, drv);
if (err)
goto fail2;
err = input_register_device(nkbd->dev);
if (err)
goto fail3;
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(nkbd);
return err;
}
static void nkbd_disconnect(struct serio *serio)
{
struct nkbd *nkbd = serio_get_drvdata(serio);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_unregister_device(nkbd->dev);
kfree(nkbd);
}
static struct serio_device_id nkbd_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_NEWTON,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, nkbd_serio_ids);
static struct serio_driver nkbd_drv = {
.driver = {
.name = "newtonkbd",
},
.description = DRIVER_DESC,
.id_table = nkbd_serio_ids,
.interrupt = nkbd_interrupt,
.connect = nkbd_connect,
.disconnect = nkbd_disconnect,
};
module_serio_driver(nkbd_drv);

View File

@@ -0,0 +1,438 @@
/*
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
*
* License terms:GNU General Public License (GPL) version 2
*
* Keypad controller driver for the SKE (Scroll Key Encoder) module used in
* the Nomadik 8815 and Ux500 platforms.
*/
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_data/keypad-nomadik-ske.h>
/* SKE_CR bits */
#define SKE_KPMLT (0x1 << 6)
#define SKE_KPCN (0x7 << 3)
#define SKE_KPASEN (0x1 << 2)
#define SKE_KPASON (0x1 << 7)
/* SKE_IMSC bits */
#define SKE_KPIMA (0x1 << 2)
/* SKE_ICR bits */
#define SKE_KPICS (0x1 << 3)
#define SKE_KPICA (0x1 << 2)
/* SKE_RIS bits */
#define SKE_KPRISA (0x1 << 2)
#define SKE_KEYPAD_ROW_SHIFT 3
#define SKE_KPD_NUM_ROWS 8
#define SKE_KPD_NUM_COLS 8
/* keypad auto scan registers */
#define SKE_ASR0 0x20
#define SKE_ASR1 0x24
#define SKE_ASR2 0x28
#define SKE_ASR3 0x2C
#define SKE_NUM_ASRX_REGISTERS (4)
#define KEY_PRESSED_DELAY 10
/**
* struct ske_keypad - data structure used by keypad driver
* @irq: irq no
* @reg_base: ske regsiters base address
* @input: pointer to input device object
* @board: keypad platform device
* @keymap: matrix scan code table for keycodes
* @clk: clock structure pointer
*/
struct ske_keypad {
int irq;
void __iomem *reg_base;
struct input_dev *input;
const struct ske_keypad_platform_data *board;
unsigned short keymap[SKE_KPD_NUM_ROWS * SKE_KPD_NUM_COLS];
struct clk *clk;
struct clk *pclk;
spinlock_t ske_keypad_lock;
};
static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
u8 mask, u8 data)
{
u32 ret;
spin_lock(&keypad->ske_keypad_lock);
ret = readl(keypad->reg_base + addr);
ret &= ~mask;
ret |= data;
writel(ret, keypad->reg_base + addr);
spin_unlock(&keypad->ske_keypad_lock);
}
/*
* ske_keypad_chip_init: init keypad controller configuration
*
* Enable Multi key press detection, auto scan mode
*/
static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
{
u32 value;
int timeout = keypad->board->debounce_ms;
/* check SKE_RIS to be 0 */
while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
cpu_relax();
if (!timeout)
return -EINVAL;
/*
* set debounce value
* keypad dbounce is configured in DBCR[15:8]
* dbounce value in steps of 32/32.768 ms
*/
spin_lock(&keypad->ske_keypad_lock);
value = readl(keypad->reg_base + SKE_DBCR);
value = value & 0xff;
value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
writel(value, keypad->reg_base + SKE_DBCR);
spin_unlock(&keypad->ske_keypad_lock);
/* enable multi key detection */
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
/*
* set up the number of columns
* KPCN[5:3] defines no. of keypad columns to be auto scanned
*/
value = (keypad->board->kcol - 1) << 3;
ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
/* clear keypad interrupt for auto(and pending SW) scans */
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
/* un-mask keypad interrupts */
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
/* enable automatic scan */
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
return 0;
}
static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
{
int row = 0, code, pos;
struct input_dev *input = keypad->input;
u32 ske_ris;
int key_pressed;
int num_of_rows;
/* find out the row */
num_of_rows = hweight8(status);
do {
pos = __ffs(status);
row = pos;
status &= ~(1 << pos);
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
ske_ris = readl(keypad->reg_base + SKE_RIS);
key_pressed = ske_ris & SKE_KPRISA;
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keypad->keymap[code], key_pressed);
input_sync(input);
num_of_rows--;
} while (num_of_rows);
}
static void ske_keypad_read_data(struct ske_keypad *keypad)
{
u8 status;
int col = 0;
int ske_asr, i;
/*
* Read the auto scan registers
*
* Each SKE_ASRx (x=0 to x=3) contains two row values.
* lower byte contains row value for column 2*x,
* upper byte contains row value for column 2*x + 1
*/
for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
if (!ske_asr)
continue;
/* now that ASRx is zero, find out the coloumn x and row y */
status = ske_asr & 0xff;
if (status) {
col = i * 2;
ske_keypad_report(keypad, status, col);
}
status = (ske_asr & 0xff00) >> 8;
if (status) {
col = (i * 2) + 1;
ske_keypad_report(keypad, status, col);
}
}
}
static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
{
struct ske_keypad *keypad = dev_id;
int timeout = keypad->board->debounce_ms;
/* disable auto scan interrupt; mask the interrupt generated */
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout)
cpu_relax();
/* SKEx registers are stable and can be read */
ske_keypad_read_data(keypad);
/* wait until raw interrupt is clear */
while ((readl(keypad->reg_base + SKE_RIS)) && --timeout)
msleep(KEY_PRESSED_DELAY);
/* enable auto scan interrupts */
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
return IRQ_HANDLED;
}
static int __init ske_keypad_probe(struct platform_device *pdev)
{
const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
struct ske_keypad *keypad;
struct input_dev *input;
struct resource *res;
int irq;
int error;
if (!plat) {
dev_err(&pdev->dev, "invalid keypad platform data\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get keypad irq\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing platform resources\n");
return -EINVAL;
}
keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
input = input_allocate_device();
if (!keypad || !input) {
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
error = -ENOMEM;
goto err_free_mem;
}
keypad->irq = irq;
keypad->board = plat;
keypad->input = input;
spin_lock_init(&keypad->ske_keypad_lock);
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto err_free_mem;
}
keypad->reg_base = ioremap(res->start, resource_size(res));
if (!keypad->reg_base) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -ENXIO;
goto err_free_mem_region;
}
keypad->pclk = clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(keypad->pclk)) {
dev_err(&pdev->dev, "failed to get pclk\n");
error = PTR_ERR(keypad->pclk);
goto err_iounmap;
}
keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get clk\n");
error = PTR_ERR(keypad->clk);
goto err_pclk;
}
input->id.bustype = BUS_HOST;
input->name = "ux500-ske-keypad";
input->dev.parent = &pdev->dev;
error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
SKE_KPD_NUM_ROWS, SKE_KPD_NUM_COLS,
keypad->keymap, input);
if (error) {
dev_err(&pdev->dev, "Failed to build keymap\n");
goto err_clk;
}
input_set_capability(input, EV_MSC, MSC_SCAN);
if (!plat->no_autorepeat)
__set_bit(EV_REP, input->evbit);
error = clk_prepare_enable(keypad->pclk);
if (error) {
dev_err(&pdev->dev, "Failed to prepare/enable pclk\n");
goto err_clk;
}
error = clk_prepare_enable(keypad->clk);
if (error) {
dev_err(&pdev->dev, "Failed to prepare/enable clk\n");
goto err_pclk_disable;
}
/* go through board initialization helpers */
if (keypad->board->init)
keypad->board->init();
error = ske_keypad_chip_init(keypad);
if (error) {
dev_err(&pdev->dev, "unable to init keypad hardware\n");
goto err_clk_disable;
}
error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
IRQF_ONESHOT, "ske-keypad", keypad);
if (error) {
dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
goto err_clk_disable;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev,
"unable to register input device: %d\n", error);
goto err_free_irq;
}
if (plat->wakeup_enable)
device_init_wakeup(&pdev->dev, true);
platform_set_drvdata(pdev, keypad);
return 0;
err_free_irq:
free_irq(keypad->irq, keypad);
err_clk_disable:
clk_disable_unprepare(keypad->clk);
err_pclk_disable:
clk_disable_unprepare(keypad->pclk);
err_clk:
clk_put(keypad->clk);
err_pclk:
clk_put(keypad->pclk);
err_iounmap:
iounmap(keypad->reg_base);
err_free_mem_region:
release_mem_region(res->start, resource_size(res));
err_free_mem:
input_free_device(input);
kfree(keypad);
return error;
}
static int ske_keypad_remove(struct platform_device *pdev)
{
struct ske_keypad *keypad = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
free_irq(keypad->irq, keypad);
input_unregister_device(keypad->input);
clk_disable_unprepare(keypad->clk);
clk_put(keypad->clk);
if (keypad->board->exit)
keypad->board->exit();
iounmap(keypad->reg_base);
release_mem_region(res->start, resource_size(res));
kfree(keypad);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int ske_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ske_keypad *keypad = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
if (device_may_wakeup(dev))
enable_irq_wake(irq);
else
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
return 0;
}
static int ske_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ske_keypad *keypad = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
if (device_may_wakeup(dev))
disable_irq_wake(irq);
else
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops,
ske_keypad_suspend, ske_keypad_resume);
static struct platform_driver ske_keypad_driver = {
.driver = {
.name = "nmk-ske-keypad",
.owner = THIS_MODULE,
.pm = &ske_keypad_dev_pm_ops,
},
.remove = ske_keypad_remove,
};
module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
MODULE_ALIAS("platform:nomadik-ske-keypad");

View File

@@ -0,0 +1,395 @@
/*
* linux/drivers/input/keyboard/omap-keypad.c
*
* OMAP Keypad Driver
*
* Copyright (C) 2003 Nokia Corporation
* Written by Timo Teräs <ext-timo.teras@nokia.com>
*
* Added support for H2 & H3 Keypad
* Copyright (C) 2004 Texas Instruments
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
#include <linux/platform_data/keypad-omap.h>
#undef NEW_BOARD_LEARNING_MODE
static void omap_kp_tasklet(unsigned long);
static void omap_kp_timer(unsigned long);
static unsigned char keypad_state[8];
static DEFINE_MUTEX(kp_enable_mutex);
static int kp_enable = 1;
static int kp_cur_group = -1;
struct omap_kp {
struct input_dev *input;
struct timer_list timer;
int irq;
unsigned int rows;
unsigned int cols;
unsigned long delay;
unsigned int debounce;
unsigned short keymap[];
};
static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
static unsigned int *row_gpios;
static unsigned int *col_gpios;
#ifdef CONFIG_ARCH_OMAP2
static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)
{
int col;
for (col = 0; col < omap_kp->cols; col++)
gpio_set_value(col_gpios[col], value & (1 << col));
}
static u8 get_row_gpio_val(struct omap_kp *omap_kp)
{
int row;
u8 value = 0;
for (row = 0; row < omap_kp->rows; row++) {
if (gpio_get_value(row_gpios[row]))
value |= (1 << row);
}
return value;
}
#else
#define set_col_gpio_val(x, y) do {} while (0)
#define get_row_gpio_val(x) 0
#endif
static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
{
/* disable keyboard interrupt and schedule for handling */
omap_writew(1, OMAP1_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(struct omap_kp *omap_kp, unsigned char *state)
{
int col = 0;
/* disable keyboard interrupt and schedule for handling */
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
/* read the keypad status */
omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
for (col = 0; col < omap_kp->cols; col++) {
omap_writew(~(1 << col) & 0xff,
OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
udelay(omap_kp->delay);
state[col] = ~omap_readw(OMAP1_MPUIO_BASE +
OMAP_MPUIO_KBR_LATCH) & 0xff;
}
omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
udelay(2);
}
static void omap_kp_tasklet(unsigned long data)
{
struct omap_kp *omap_kp_data = (struct omap_kp *) data;
unsigned short *keycodes = omap_kp_data->input->keycode;
unsigned int row_shift = get_count_order(omap_kp_data->cols);
unsigned char new_state[8], changed, key_down = 0;
int col, row;
int spurious = 0;
/* check for any changes */
omap_kp_scan_keypad(omap_kp_data, new_state);
/* check for changes and print those */
for (col = 0; col < omap_kp_data->cols; col++) {
changed = new_state[col] ^ keypad_state[col];
key_down |= new_state[col];
if (changed == 0)
continue;
for (row = 0; row < omap_kp_data->rows; 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 = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
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;
}
if (!(kp_cur_group == (key & GROUP_MASK) ||
kp_cur_group == -1))
continue;
kp_cur_group = key & GROUP_MASK;
input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
new_state[col] & (1 << row));
#endif
}
}
input_sync(omap_kp_data->input);
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(&omap_kp_data->timer, jiffies + delay);
} else {
/* enable interrupts */
omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
kp_cur_group = -1;
}
}
static ssize_t omap_kp_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%u\n", kp_enable);
}
static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct omap_kp *omap_kp = dev_get_drvdata(dev);
int state;
if (sscanf(buf, "%u", &state) != 1)
return -EINVAL;
if ((state != 1) && (state != 0))
return -EINVAL;
mutex_lock(&kp_enable_mutex);
if (state != kp_enable) {
if (state)
enable_irq(omap_kp->irq);
else
disable_irq(omap_kp->irq);
kp_enable = state;
}
mutex_unlock(&kp_enable_mutex);
return strnlen(buf, count);
}
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
#ifdef CONFIG_PM
static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
{
/* Nothing yet */
return 0;
}
static int omap_kp_resume(struct platform_device *dev)
{
/* Nothing yet */
return 0;
}
#else
#define omap_kp_suspend NULL
#define omap_kp_resume NULL
#endif
static int omap_kp_probe(struct platform_device *pdev)
{
struct omap_kp *omap_kp;
struct input_dev *input_dev;
struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
int i, col_idx, row_idx, ret;
unsigned int row_shift, keycodemax;
if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
printk(KERN_ERR "No rows, cols or keymap_data from pdata\n");
return -EINVAL;
}
row_shift = get_count_order(pdata->cols);
keycodemax = pdata->rows << row_shift;
omap_kp = kzalloc(sizeof(struct omap_kp) +
keycodemax * sizeof(unsigned short), GFP_KERNEL);
input_dev = input_allocate_device();
if (!omap_kp || !input_dev) {
kfree(omap_kp);
input_free_device(input_dev);
return -ENOMEM;
}
platform_set_drvdata(pdev, omap_kp);
omap_kp->input = input_dev;
/* Disable the interrupt for the MPUIO keyboard */
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
if (pdata->delay)
omap_kp->delay = pdata->delay;
if (pdata->row_gpios && pdata->col_gpios) {
row_gpios = pdata->row_gpios;
col_gpios = pdata->col_gpios;
}
omap_kp->rows = pdata->rows;
omap_kp->cols = pdata->cols;
col_idx = 0;
row_idx = 0;
setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
/* get the irq and init timer*/
tasklet_enable(&kp_tasklet);
kp_tasklet.data = (unsigned long) omap_kp;
ret = device_create_file(&pdev->dev, &dev_attr_enable);
if (ret < 0)
goto err2;
/* setup input device */
input_dev->name = "omap-keypad";
input_dev->phys = "omap-keypad/input0";
input_dev->dev.parent = &pdev->dev;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
if (pdata->rep)
__set_bit(EV_REP, input_dev->evbit);
ret = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
pdata->rows, pdata->cols,
omap_kp->keymap, input_dev);
if (ret < 0)
goto err3;
ret = input_register_device(omap_kp->input);
if (ret < 0) {
printk(KERN_ERR "Unable to register omap-keypad input device\n");
goto err3;
}
if (pdata->dbounce)
omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
/* scan current status and enable interrupt */
omap_kp_scan_keypad(omap_kp, keypad_state);
omap_kp->irq = platform_get_irq(pdev, 0);
if (omap_kp->irq >= 0) {
if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
"omap-keypad", omap_kp) < 0)
goto err4;
}
omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
return 0;
err4:
input_unregister_device(omap_kp->input);
input_dev = NULL;
err3:
device_remove_file(&pdev->dev, &dev_attr_enable);
err2:
for (i = row_idx - 1; i >= 0; i--)
gpio_free(row_gpios[i]);
for (i = col_idx - 1; i >= 0; i--)
gpio_free(col_gpios[i]);
kfree(omap_kp);
input_free_device(input_dev);
return -EINVAL;
}
static int omap_kp_remove(struct platform_device *pdev)
{
struct omap_kp *omap_kp = platform_get_drvdata(pdev);
/* disable keypad interrupt handling */
tasklet_disable(&kp_tasklet);
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
free_irq(omap_kp->irq, omap_kp);
del_timer_sync(&omap_kp->timer);
tasklet_kill(&kp_tasklet);
/* unregister everything */
input_unregister_device(omap_kp->input);
kfree(omap_kp);
return 0;
}
static struct platform_driver omap_kp_driver = {
.probe = omap_kp_probe,
.remove = omap_kp_remove,
.suspend = omap_kp_suspend,
.resume = omap_kp_resume,
.driver = {
.name = "omap-keypad",
.owner = THIS_MODULE,
},
};
module_platform_driver(omap_kp_driver);
MODULE_AUTHOR("Timo Teräs");
MODULE_DESCRIPTION("OMAP Keypad Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:omap-keypad");

View File

@@ -0,0 +1,449 @@
/*
* OMAP4 Keypad Driver
*
* Copyright (C) 2010 Texas Instruments
*
* Author: Abraham Arce <x0066660@ti.com>
* Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.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
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/omap4-keypad.h>
/* OMAP4 registers */
#define OMAP4_KBD_REVISION 0x00
#define OMAP4_KBD_SYSCONFIG 0x10
#define OMAP4_KBD_SYSSTATUS 0x14
#define OMAP4_KBD_IRQSTATUS 0x18
#define OMAP4_KBD_IRQENABLE 0x1C
#define OMAP4_KBD_WAKEUPENABLE 0x20
#define OMAP4_KBD_PENDING 0x24
#define OMAP4_KBD_CTRL 0x28
#define OMAP4_KBD_DEBOUNCINGTIME 0x2C
#define OMAP4_KBD_LONGKEYTIME 0x30
#define OMAP4_KBD_TIMEOUT 0x34
#define OMAP4_KBD_STATEMACHINE 0x38
#define OMAP4_KBD_ROWINPUTS 0x3C
#define OMAP4_KBD_COLUMNOUTPUTS 0x40
#define OMAP4_KBD_FULLCODE31_0 0x44
#define OMAP4_KBD_FULLCODE63_32 0x48
/* OMAP4 bit definitions */
#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0)
#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1)
#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2)
#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0)
#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1)
#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1)
#define OMAP4_DEF_CTRLPTVVALUE (1 << 2)
#define OMAP4_DEF_CTRLPTV (1 << 1)
/* OMAP4 values */
#define OMAP4_VAL_IRQDISABLE 0x00
#define OMAP4_VAL_DEBOUNCINGTIME 0x07
#define OMAP4_VAL_FUNCTIONALCFG 0x1E
#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF
enum {
KBD_REVISION_OMAP4 = 0,
KBD_REVISION_OMAP5,
};
struct omap4_keypad {
struct input_dev *input;
void __iomem *base;
unsigned int irq;
unsigned int rows;
unsigned int cols;
u32 reg_offset;
u32 irqreg_offset;
unsigned int row_shift;
bool no_autorepeat;
unsigned char key_state[8];
unsigned short *keymap;
};
static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
{
return __raw_readl(keypad_data->base +
keypad_data->reg_offset + offset);
}
static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
{
__raw_writel(value,
keypad_data->base + keypad_data->reg_offset + offset);
}
static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
{
return __raw_readl(keypad_data->base +
keypad_data->irqreg_offset + offset);
}
static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
u32 offset, u32 value)
{
__raw_writel(value,
keypad_data->base + keypad_data->irqreg_offset + offset);
}
/* Interrupt handler */
static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
{
struct omap4_keypad *keypad_data = dev_id;
struct input_dev *input_dev = keypad_data->input;
unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
unsigned int col, row, code, changed;
u32 *new_state = (u32 *) key_state;
/* Disable interrupts */
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
OMAP4_VAL_IRQDISABLE);
*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
for (row = 0; row < keypad_data->rows; row++) {
changed = key_state[row] ^ keypad_data->key_state[row];
if (!changed)
continue;
for (col = 0; col < keypad_data->cols; col++) {
if (changed & (1 << col)) {
code = MATRIX_SCAN_CODE(row, col,
keypad_data->row_shift);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev,
keypad_data->keymap[code],
key_state[row] & (1 << col));
}
}
}
input_sync(input_dev);
memcpy(keypad_data->key_state, key_state,
sizeof(keypad_data->key_state));
/* clear pending interrupts */
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
/* enable interrupts */
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
OMAP4_DEF_IRQENABLE_EVENTEN |
OMAP4_DEF_IRQENABLE_LONGKEY);
return IRQ_HANDLED;
}
static int omap4_keypad_open(struct input_dev *input)
{
struct omap4_keypad *keypad_data = input_get_drvdata(input);
pm_runtime_get_sync(input->dev.parent);
disable_irq(keypad_data->irq);
kbd_writel(keypad_data, OMAP4_KBD_CTRL,
OMAP4_VAL_FUNCTIONALCFG);
kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
OMAP4_VAL_DEBOUNCINGTIME);
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
OMAP4_VAL_IRQDISABLE);
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
OMAP4_DEF_IRQENABLE_EVENTEN |
OMAP4_DEF_IRQENABLE_LONGKEY);
kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
enable_irq(keypad_data->irq);
return 0;
}
static void omap4_keypad_close(struct input_dev *input)
{
struct omap4_keypad *keypad_data = input_get_drvdata(input);
disable_irq(keypad_data->irq);
/* Disable interrupts */
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
OMAP4_VAL_IRQDISABLE);
/* clear pending interrupts */
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
enable_irq(keypad_data->irq);
pm_runtime_put_sync(input->dev.parent);
}
#ifdef CONFIG_OF
static int omap4_keypad_parse_dt(struct device *dev,
struct omap4_keypad *keypad_data)
{
struct device_node *np = dev->of_node;
int err;
err = matrix_keypad_parse_of_params(dev, &keypad_data->rows,
&keypad_data->cols);
if (err)
return err;
if (of_get_property(np, "linux,input-no-autorepeat", NULL))
keypad_data->no_autorepeat = true;
return 0;
}
#else
static inline int omap4_keypad_parse_dt(struct device *dev,
struct omap4_keypad *keypad_data)
{
return -ENOSYS;
}
#endif
static int omap4_keypad_probe(struct platform_device *pdev)
{
const struct omap4_keypad_platform_data *pdata =
dev_get_platdata(&pdev->dev);
const struct matrix_keymap_data *keymap_data =
pdata ? pdata->keymap_data : NULL;
struct omap4_keypad *keypad_data;
struct input_dev *input_dev;
struct resource *res;
unsigned int max_keys;
int rev;
int irq;
int error;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no base address specified\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (!irq) {
dev_err(&pdev->dev, "no keyboard irq assigned\n");
return -EINVAL;
}
keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
if (!keypad_data) {
dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
return -ENOMEM;
}
keypad_data->irq = irq;
if (pdata) {
keypad_data->rows = pdata->rows;
keypad_data->cols = pdata->cols;
} else {
error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
if (error)
return error;
}
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (!res) {
dev_err(&pdev->dev, "can't request mem region\n");
error = -EBUSY;
goto err_free_keypad;
}
keypad_data->base = ioremap(res->start, resource_size(res));
if (!keypad_data->base) {
dev_err(&pdev->dev, "can't ioremap mem resource\n");
error = -ENOMEM;
goto err_release_mem;
}
/*
* Enable clocks for the keypad module so that we can read
* revision register.
*/
pm_runtime_enable(&pdev->dev);
error = pm_runtime_get_sync(&pdev->dev);
if (error) {
dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
goto err_unmap;
}
rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
rev &= 0x03 << 30;
rev >>= 30;
switch (rev) {
case KBD_REVISION_OMAP4:
keypad_data->reg_offset = 0x00;
keypad_data->irqreg_offset = 0x00;
break;
case KBD_REVISION_OMAP5:
keypad_data->reg_offset = 0x10;
keypad_data->irqreg_offset = 0x0c;
break;
default:
dev_err(&pdev->dev,
"Keypad reports unsupported revision %d", rev);
error = -EINVAL;
goto err_pm_put_sync;
}
/* input device allocation */
keypad_data->input = input_dev = input_allocate_device();
if (!input_dev) {
error = -ENOMEM;
goto err_pm_put_sync;
}
input_dev->name = pdev->name;
input_dev->dev.parent = &pdev->dev;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0001;
input_dev->open = omap4_keypad_open;
input_dev->close = omap4_keypad_close;
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
if (!keypad_data->no_autorepeat)
__set_bit(EV_REP, input_dev->evbit);
input_set_drvdata(input_dev, keypad_data);
keypad_data->row_shift = get_count_order(keypad_data->cols);
max_keys = keypad_data->rows << keypad_data->row_shift;
keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]),
GFP_KERNEL);
if (!keypad_data->keymap) {
dev_err(&pdev->dev, "Not enough memory for keymap\n");
error = -ENOMEM;
goto err_free_input;
}
error = matrix_keypad_build_keymap(keymap_data, NULL,
keypad_data->rows, keypad_data->cols,
keypad_data->keymap, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_free_keymap;
}
error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
IRQF_TRIGGER_RISING,
"omap4-keypad", keypad_data);
if (error) {
dev_err(&pdev->dev, "failed to register interrupt\n");
goto err_free_input;
}
pm_runtime_put_sync(&pdev->dev);
error = input_register_device(keypad_data->input);
if (error < 0) {
dev_err(&pdev->dev, "failed to register input device\n");
goto err_pm_disable;
}
platform_set_drvdata(pdev, keypad_data);
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
free_irq(keypad_data->irq, keypad_data);
err_free_keymap:
kfree(keypad_data->keymap);
err_free_input:
input_free_device(input_dev);
err_pm_put_sync:
pm_runtime_put_sync(&pdev->dev);
err_unmap:
iounmap(keypad_data->base);
err_release_mem:
release_mem_region(res->start, resource_size(res));
err_free_keypad:
kfree(keypad_data);
return error;
}
static int omap4_keypad_remove(struct platform_device *pdev)
{
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
struct resource *res;
free_irq(keypad_data->irq, keypad_data);
pm_runtime_disable(&pdev->dev);
input_unregister_device(keypad_data->input);
iounmap(keypad_data->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
kfree(keypad_data->keymap);
kfree(keypad_data);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id omap_keypad_dt_match[] = {
{ .compatible = "ti,omap4-keypad" },
{},
};
MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
#endif
static struct platform_driver omap4_keypad_driver = {
.probe = omap4_keypad_probe,
.remove = omap4_keypad_remove,
.driver = {
.name = "omap4-keypad",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(omap_keypad_dt_match),
},
};
module_platform_driver(omap4_keypad_driver);
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("OMAP4 Keypad Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:omap4-keypad");

View File

@@ -0,0 +1,170 @@
/*
* OpenCores Keyboard Controller Driver
* http://www.opencores.org/project,keyboardcontroller
*
* Copyright 2007-2009 HV Sistemas S.L.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct opencores_kbd {
struct input_dev *input;
struct resource *addr_res;
void __iomem *addr;
int irq;
unsigned short keycodes[128];
};
static irqreturn_t opencores_kbd_isr(int irq, void *dev_id)
{
struct opencores_kbd *opencores_kbd = dev_id;
struct input_dev *input = opencores_kbd->input;
unsigned char c;
c = readb(opencores_kbd->addr);
input_report_key(input, c & 0x7f, c & 0x80 ? 0 : 1);
input_sync(input);
return IRQ_HANDLED;
}
static int opencores_kbd_probe(struct platform_device *pdev)
{
struct input_dev *input;
struct opencores_kbd *opencores_kbd;
struct resource *res;
int irq, i, error;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing board memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "missing board IRQ resource\n");
return -EINVAL;
}
opencores_kbd = kzalloc(sizeof(*opencores_kbd), GFP_KERNEL);
input = input_allocate_device();
if (!opencores_kbd || !input) {
dev_err(&pdev->dev, "failed to allocate device structures\n");
error = -ENOMEM;
goto err_free_mem;
}
opencores_kbd->addr_res = res;
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (!res) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto err_free_mem;
}
opencores_kbd->addr = ioremap(res->start, resource_size(res));
if (!opencores_kbd->addr) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -ENXIO;
goto err_rel_mem;
}
opencores_kbd->input = input;
opencores_kbd->irq = irq;
input->name = pdev->name;
input->phys = "opencores-kbd/input0";
input->dev.parent = &pdev->dev;
input_set_drvdata(input, opencores_kbd);
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->keycode = opencores_kbd->keycodes;
input->keycodesize = sizeof(opencores_kbd->keycodes[0]);
input->keycodemax = ARRAY_SIZE(opencores_kbd->keycodes);
__set_bit(EV_KEY, input->evbit);
for (i = 0; i < ARRAY_SIZE(opencores_kbd->keycodes); i++) {
/*
* OpenCores controller happens to have scancodes match
* our KEY_* definitions.
*/
opencores_kbd->keycodes[i] = i;
__set_bit(opencores_kbd->keycodes[i], input->keybit);
}
__clear_bit(KEY_RESERVED, input->keybit);
error = request_irq(irq, &opencores_kbd_isr,
IRQF_TRIGGER_RISING, pdev->name, opencores_kbd);
if (error) {
dev_err(&pdev->dev, "unable to claim irq %d\n", irq);
goto err_unmap_mem;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "unable to register input device\n");
goto err_free_irq;
}
platform_set_drvdata(pdev, opencores_kbd);
return 0;
err_free_irq:
free_irq(irq, opencores_kbd);
err_unmap_mem:
iounmap(opencores_kbd->addr);
err_rel_mem:
release_mem_region(res->start, resource_size(res));
err_free_mem:
input_free_device(input);
kfree(opencores_kbd);
return error;
}
static int opencores_kbd_remove(struct platform_device *pdev)
{
struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev);
free_irq(opencores_kbd->irq, opencores_kbd);
iounmap(opencores_kbd->addr);
release_mem_region(opencores_kbd->addr_res->start,
resource_size(opencores_kbd->addr_res));
input_unregister_device(opencores_kbd->input);
kfree(opencores_kbd);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver opencores_kbd_device_driver = {
.probe = opencores_kbd_probe,
.remove = opencores_kbd_remove,
.driver = {
.name = "opencores-kbd",
},
};
module_platform_driver(opencores_kbd_device_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>");
MODULE_DESCRIPTION("Keyboard driver for OpenCores Keyboard Controller");

View File

@@ -0,0 +1,789 @@
/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mfd/pm8xxx/core.h>
#include <linux/mfd/pm8xxx/gpio.h>
#include <linux/input/pmic8xxx-keypad.h>
#define PM8XXX_MAX_ROWS 18
#define PM8XXX_MAX_COLS 8
#define PM8XXX_ROW_SHIFT 3
#define PM8XXX_MATRIX_MAX_SIZE (PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS)
#define PM8XXX_MIN_ROWS 5
#define PM8XXX_MIN_COLS 5
#define MAX_SCAN_DELAY 128
#define MIN_SCAN_DELAY 1
/* in nanoseconds */
#define MAX_ROW_HOLD_DELAY 122000
#define MIN_ROW_HOLD_DELAY 30500
#define MAX_DEBOUNCE_TIME 20
#define MIN_DEBOUNCE_TIME 5
#define KEYP_CTRL 0x148
#define KEYP_CTRL_EVNTS BIT(0)
#define KEYP_CTRL_EVNTS_MASK 0x3
#define KEYP_CTRL_SCAN_COLS_SHIFT 5
#define KEYP_CTRL_SCAN_COLS_MIN 5
#define KEYP_CTRL_SCAN_COLS_BITS 0x3
#define KEYP_CTRL_SCAN_ROWS_SHIFT 2
#define KEYP_CTRL_SCAN_ROWS_MIN 5
#define KEYP_CTRL_SCAN_ROWS_BITS 0x7
#define KEYP_CTRL_KEYP_EN BIT(7)
#define KEYP_SCAN 0x149
#define KEYP_SCAN_READ_STATE BIT(0)
#define KEYP_SCAN_DBOUNCE_SHIFT 1
#define KEYP_SCAN_PAUSE_SHIFT 3
#define KEYP_SCAN_ROW_HOLD_SHIFT 6
#define KEYP_TEST 0x14A
#define KEYP_TEST_CLEAR_RECENT_SCAN BIT(6)
#define KEYP_TEST_CLEAR_OLD_SCAN BIT(5)
#define KEYP_TEST_READ_RESET BIT(4)
#define KEYP_TEST_DTEST_EN BIT(3)
#define KEYP_TEST_ABORT_READ BIT(0)
#define KEYP_TEST_DBG_SELECT_SHIFT 1
/* bits of these registers represent
* '0' for key press
* '1' for key release
*/
#define KEYP_RECENT_DATA 0x14B
#define KEYP_OLD_DATA 0x14C
#define KEYP_CLOCK_FREQ 32768
/**
* struct pmic8xxx_kp - internal keypad data structure
* @pdata - keypad platform data pointer
* @input - input device pointer for keypad
* @key_sense_irq - key press/release irq number
* @key_stuck_irq - key stuck notification irq number
* @keycodes - array to hold the key codes
* @dev - parent device pointer
* @keystate - present key press/release state
* @stuckstate - present state when key stuck irq
* @ctrl_reg - control register value
*/
struct pmic8xxx_kp {
const struct pm8xxx_keypad_platform_data *pdata;
struct input_dev *input;
int key_sense_irq;
int key_stuck_irq;
unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE];
struct device *dev;
u16 keystate[PM8XXX_MAX_ROWS];
u16 stuckstate[PM8XXX_MAX_ROWS];
u8 ctrl_reg;
};
static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp,
u8 data, u16 reg)
{
int rc;
rc = pm8xxx_writeb(kp->dev->parent, reg, data);
return rc;
}
static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp,
u8 *data, u16 reg, unsigned num_bytes)
{
int rc;
rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes);
return rc;
}
static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp,
u8 *data, u16 reg)
{
int rc;
rc = pmic8xxx_kp_read(kp, data, reg, 1);
return rc;
}
static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
{
/* all keys pressed on that particular row? */
if (col == 0x00)
return 1 << kp->pdata->num_cols;
else
return col & ((1 << kp->pdata->num_cols) - 1);
}
/*
* Synchronous read protocol for RevB0 onwards:
*
* 1. Write '1' to ReadState bit in KEYP_SCAN register
* 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode
* synchronously
* 3. Read rows in old array first if events are more than one
* 4. Read rows in recent array
* 5. Wait 4*32KHz clocks
* 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can
* synchronously exit read mode.
*/
static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
{
int rc;
u8 scan_val;
rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
if (rc < 0) {
dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
return rc;
}
scan_val |= 0x1;
rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
if (rc < 0) {
dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
return rc;
}
/* 2 * 32KHz clocks */
udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
return rc;
}
static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state,
u16 data_reg, int read_rows)
{
int rc, row;
u8 new_data[PM8XXX_MAX_ROWS];
rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows);
if (rc)
return rc;
for (row = 0; row < kp->pdata->num_rows; row++) {
dev_dbg(kp->dev, "new_data[%d] = %d\n", row,
new_data[row]);
state[row] = pmic8xxx_col_state(kp, new_data[row]);
}
return rc;
}
static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
u16 *old_state)
{
int rc, read_rows;
u8 scan_val;
if (kp->pdata->num_rows < PM8XXX_MIN_ROWS)
read_rows = PM8XXX_MIN_ROWS;
else
read_rows = kp->pdata->num_rows;
pmic8xxx_chk_sync_read(kp);
if (old_state) {
rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA,
read_rows);
if (rc < 0) {
dev_err(kp->dev,
"Error reading KEYP_OLD_DATA, rc=%d\n", rc);
return rc;
}
}
rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA,
read_rows);
if (rc < 0) {
dev_err(kp->dev,
"Error reading KEYP_RECENT_DATA, rc=%d\n", rc);
return rc;
}
/* 4 * 32KHz clocks */
udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
if (rc < 0) {
dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
return rc;
}
scan_val &= 0xFE;
rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
if (rc < 0)
dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
return rc;
}
static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
u16 *old_state)
{
int row, col, code;
for (row = 0; row < kp->pdata->num_rows; row++) {
int bits_changed = new_state[row] ^ old_state[row];
if (!bits_changed)
continue;
for (col = 0; col < kp->pdata->num_cols; col++) {
if (!(bits_changed & (1 << col)))
continue;
dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col,
!(new_state[row] & (1 << col)) ?
"pressed" : "released");
code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT);
input_event(kp->input, EV_MSC, MSC_SCAN, code);
input_report_key(kp->input,
kp->keycodes[code],
!(new_state[row] & (1 << col)));
input_sync(kp->input);
}
}
}
static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state)
{
int row, found_first = -1;
u16 check, row_state;
check = 0;
for (row = 0; row < kp->pdata->num_rows; row++) {
row_state = (~new_state[row]) &
((1 << kp->pdata->num_cols) - 1);
if (hweight16(row_state) > 1) {
if (found_first == -1)
found_first = row;
if (check & row_state) {
dev_dbg(kp->dev, "detected ghost key on row[%d]"
" and row[%d]\n", found_first, row);
return true;
}
}
check |= row_state;
}
return false;
}
static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int events)
{
u16 new_state[PM8XXX_MAX_ROWS];
u16 old_state[PM8XXX_MAX_ROWS];
int rc;
switch (events) {
case 0x1:
rc = pmic8xxx_kp_read_matrix(kp, new_state, NULL);
if (rc < 0)
return rc;
/* detecting ghost key is not an error */
if (pmic8xxx_detect_ghost_keys(kp, new_state))
return 0;
__pmic8xxx_kp_scan_matrix(kp, new_state, kp->keystate);
memcpy(kp->keystate, new_state, sizeof(new_state));
break;
case 0x3: /* two events - eventcounter is gray-coded */
rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
if (rc < 0)
return rc;
__pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate);
__pmic8xxx_kp_scan_matrix(kp, new_state, old_state);
memcpy(kp->keystate, new_state, sizeof(new_state));
break;
case 0x2:
dev_dbg(kp->dev, "Some key events were lost\n");
rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
if (rc < 0)
return rc;
__pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate);
__pmic8xxx_kp_scan_matrix(kp, new_state, old_state);
memcpy(kp->keystate, new_state, sizeof(new_state));
break;
default:
rc = -EINVAL;
}
return rc;
}
/*
* NOTE: We are reading recent and old data registers blindly
* whenever key-stuck interrupt happens, because events counter doesn't
* get updated when this interrupt happens due to key stuck doesn't get
* considered as key state change.
*
* We are not using old data register contents after they are being read
* because it might report the key which was pressed before the key being stuck
* as stuck key because it's pressed status is stored in the old data
* register.
*/
static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data)
{
u16 new_state[PM8XXX_MAX_ROWS];
u16 old_state[PM8XXX_MAX_ROWS];
int rc;
struct pmic8xxx_kp *kp = data;
rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
if (rc < 0) {
dev_err(kp->dev, "failed to read keypad matrix\n");
return IRQ_HANDLED;
}
__pmic8xxx_kp_scan_matrix(kp, new_state, kp->stuckstate);
return IRQ_HANDLED;
}
static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
{
struct pmic8xxx_kp *kp = data;
u8 ctrl_val, events;
int rc;
rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1);
if (rc < 0) {
dev_err(kp->dev, "failed to read keyp_ctrl register\n");
return IRQ_HANDLED;
}
events = ctrl_val & KEYP_CTRL_EVNTS_MASK;
rc = pmic8xxx_kp_scan_matrix(kp, events);
if (rc < 0)
dev_err(kp->dev, "failed to scan matrix\n");
return IRQ_HANDLED;
}
static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
{
int bits, rc, cycles;
u8 scan_val = 0, ctrl_val = 0;
static const u8 row_bits[] = {
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
};
/* Find column bits */
if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
bits = 0;
else
bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) <<
KEYP_CTRL_SCAN_COLS_SHIFT;
/* Find row bits */
if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
bits = 0;
else
bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL);
if (rc < 0) {
dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
return rc;
}
bits = (kp->pdata->debounce_ms / 5) - 1;
scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT);
bits = fls(kp->pdata->scan_delay_ms) - 1;
scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT);
/* Row hold time is a multiple of 32KHz cycles. */
cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
if (rc)
dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
return rc;
}
static int pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios,
struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config)
{
int rc, i;
if (gpio_start < 0 || num_gpios < 0)
return -EINVAL;
for (i = 0; i < num_gpios; i++) {
rc = pm8xxx_gpio_config(gpio_start + i, gpio_config);
if (rc) {
dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():"
"for PM GPIO [%d] rc=%d.\n",
__func__, gpio_start + i, rc);
return rc;
}
}
return 0;
}
static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp)
{
int rc;
kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
if (rc < 0)
dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
return rc;
}
static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp)
{
int rc;
kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
if (rc < 0)
return rc;
return rc;
}
static int pmic8xxx_kp_open(struct input_dev *dev)
{
struct pmic8xxx_kp *kp = input_get_drvdata(dev);
return pmic8xxx_kp_enable(kp);
}
static void pmic8xxx_kp_close(struct input_dev *dev)
{
struct pmic8xxx_kp *kp = input_get_drvdata(dev);
pmic8xxx_kp_disable(kp);
}
/*
* keypad controller should be initialized in the following sequence
* only, otherwise it might get into FSM stuck state.
*
* - Initialize keypad control parameters, like no. of rows, columns,
* timing values etc.,
* - configure rows and column gpios pull up/down.
* - set irq edge type.
* - enable the keypad controller.
*/
static int pmic8xxx_kp_probe(struct platform_device *pdev)
{
const struct pm8xxx_keypad_platform_data *pdata =
dev_get_platdata(&pdev->dev);
const struct matrix_keymap_data *keymap_data;
struct pmic8xxx_kp *kp;
int rc;
u8 ctrl_val;
struct pm_gpio kypd_drv = {
.direction = PM_GPIO_DIR_OUT,
.output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN,
.output_value = 0,
.pull = PM_GPIO_PULL_NO,
.vin_sel = PM_GPIO_VIN_S3,
.out_strength = PM_GPIO_STRENGTH_LOW,
.function = PM_GPIO_FUNC_1,
.inv_int_pol = 1,
};
struct pm_gpio kypd_sns = {
.direction = PM_GPIO_DIR_IN,
.pull = PM_GPIO_PULL_UP_31P5,
.vin_sel = PM_GPIO_VIN_S3,
.out_strength = PM_GPIO_STRENGTH_NO,
.function = PM_GPIO_FUNC_NORMAL,
.inv_int_pol = 1,
};
if (!pdata || !pdata->num_cols || !pdata->num_rows ||
pdata->num_cols > PM8XXX_MAX_COLS ||
pdata->num_rows > PM8XXX_MAX_ROWS ||
pdata->num_cols < PM8XXX_MIN_COLS) {
dev_err(&pdev->dev, "invalid platform data\n");
return -EINVAL;
}
if (!pdata->scan_delay_ms ||
pdata->scan_delay_ms > MAX_SCAN_DELAY ||
pdata->scan_delay_ms < MIN_SCAN_DELAY ||
!is_power_of_2(pdata->scan_delay_ms)) {
dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
return -EINVAL;
}
if (!pdata->row_hold_ns ||
pdata->row_hold_ns > MAX_ROW_HOLD_DELAY ||
pdata->row_hold_ns < MIN_ROW_HOLD_DELAY ||
((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
return -EINVAL;
}
if (!pdata->debounce_ms ||
((pdata->debounce_ms % 5) != 0) ||
pdata->debounce_ms > MAX_DEBOUNCE_TIME ||
pdata->debounce_ms < MIN_DEBOUNCE_TIME) {
dev_err(&pdev->dev, "invalid debounce time supplied\n");
return -EINVAL;
}
keymap_data = pdata->keymap_data;
if (!keymap_data) {
dev_err(&pdev->dev, "no keymap data supplied\n");
return -EINVAL;
}
kp = kzalloc(sizeof(*kp), GFP_KERNEL);
if (!kp)
return -ENOMEM;
platform_set_drvdata(pdev, kp);
kp->pdata = pdata;
kp->dev = &pdev->dev;
kp->input = input_allocate_device();
if (!kp->input) {
dev_err(&pdev->dev, "unable to allocate input device\n");
rc = -ENOMEM;
goto err_alloc_device;
}
kp->key_sense_irq = platform_get_irq(pdev, 0);
if (kp->key_sense_irq < 0) {
dev_err(&pdev->dev, "unable to get keypad sense irq\n");
rc = -ENXIO;
goto err_get_irq;
}
kp->key_stuck_irq = platform_get_irq(pdev, 1);
if (kp->key_stuck_irq < 0) {
dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
rc = -ENXIO;
goto err_get_irq;
}
kp->input->name = pdata->input_name ? : "PMIC8XXX keypad";
kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0";
kp->input->dev.parent = &pdev->dev;
kp->input->id.bustype = BUS_I2C;
kp->input->id.version = 0x0001;
kp->input->id.product = 0x0001;
kp->input->id.vendor = 0x0001;
kp->input->open = pmic8xxx_kp_open;
kp->input->close = pmic8xxx_kp_close;
rc = matrix_keypad_build_keymap(keymap_data, NULL,
PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS,
kp->keycodes, kp->input);
if (rc) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_get_irq;
}
if (pdata->rep)
__set_bit(EV_REP, kp->input->evbit);
input_set_capability(kp->input, EV_MSC, MSC_SCAN);
input_set_drvdata(kp->input, kp);
/* initialize keypad state */
memset(kp->keystate, 0xff, sizeof(kp->keystate));
memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate));
rc = pmic8xxx_kpd_init(kp);
if (rc < 0) {
dev_err(&pdev->dev, "unable to initialize keypad controller\n");
goto err_get_irq;
}
rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start,
pdata->num_cols, kp, &kypd_sns);
if (rc < 0) {
dev_err(&pdev->dev, "unable to configure keypad sense lines\n");
goto err_gpio_config;
}
rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start,
pdata->num_rows, kp, &kypd_drv);
if (rc < 0) {
dev_err(&pdev->dev, "unable to configure keypad drive lines\n");
goto err_gpio_config;
}
rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq,
IRQF_TRIGGER_RISING, "pmic-keypad", kp);
if (rc < 0) {
dev_err(&pdev->dev, "failed to request keypad sense irq\n");
goto err_get_irq;
}
rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq,
IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp);
if (rc < 0) {
dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
goto err_req_stuck_irq;
}
rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
if (rc < 0) {
dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
goto err_pmic_reg_read;
}
kp->ctrl_reg = ctrl_val;
rc = input_register_device(kp->input);
if (rc < 0) {
dev_err(&pdev->dev, "unable to register keypad input device\n");
goto err_pmic_reg_read;
}
device_init_wakeup(&pdev->dev, pdata->wakeup);
return 0;
err_pmic_reg_read:
free_irq(kp->key_stuck_irq, kp);
err_req_stuck_irq:
free_irq(kp->key_sense_irq, kp);
err_gpio_config:
err_get_irq:
input_free_device(kp->input);
err_alloc_device:
platform_set_drvdata(pdev, NULL);
kfree(kp);
return rc;
}
static int pmic8xxx_kp_remove(struct platform_device *pdev)
{
struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
free_irq(kp->key_stuck_irq, kp);
free_irq(kp->key_sense_irq, kp);
input_unregister_device(kp->input);
kfree(kp);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int pmic8xxx_kp_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
struct input_dev *input_dev = kp->input;
if (device_may_wakeup(dev)) {
enable_irq_wake(kp->key_sense_irq);
} else {
mutex_lock(&input_dev->mutex);
if (input_dev->users)
pmic8xxx_kp_disable(kp);
mutex_unlock(&input_dev->mutex);
}
return 0;
}
static int pmic8xxx_kp_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
struct input_dev *input_dev = kp->input;
if (device_may_wakeup(dev)) {
disable_irq_wake(kp->key_sense_irq);
} else {
mutex_lock(&input_dev->mutex);
if (input_dev->users)
pmic8xxx_kp_enable(kp);
mutex_unlock(&input_dev->mutex);
}
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
pmic8xxx_kp_suspend, pmic8xxx_kp_resume);
static struct platform_driver pmic8xxx_kp_driver = {
.probe = pmic8xxx_kp_probe,
.remove = pmic8xxx_kp_remove,
.driver = {
.name = PM8XXX_KEYPAD_DEV_NAME,
.owner = THIS_MODULE,
.pm = &pm8xxx_kp_pm_ops,
},
};
module_platform_driver(pmic8xxx_kp_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("PMIC8XXX keypad driver");
MODULE_VERSION("1.0");
MODULE_ALIAS("platform:pmic8xxx_keypad");
MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");

View File

@@ -0,0 +1,635 @@
/*
* linux/drivers/input/keyboard/pxa27x_keypad.c
*
* Driver for the pxa27x matrix keyboard controller.
*
* Created: Feb 22, 2007
* Author: Rodolfo Giometti <giometti@linux.it>
*
* Based on a previous implementations by Kevin O'Connor
* <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
* on some suggestions by Nicolas Pitre <nico@fluxnic.net>.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/hardware.h>
#include <linux/platform_data/keypad-pxa27x.h>
/*
* Keypad Controller registers
*/
#define KPC 0x0000 /* Keypad Control register */
#define KPDK 0x0008 /* Keypad Direct Key register */
#define KPREC 0x0010 /* Keypad Rotary Encoder register */
#define KPMK 0x0018 /* Keypad Matrix Key register */
#define KPAS 0x0020 /* Keypad Automatic Scan register */
/* Keypad Automatic Scan Multiple Key Presser register 0-3 */
#define KPASMKP0 0x0028
#define KPASMKP1 0x0030
#define KPASMKP2 0x0038
#define KPASMKP3 0x0040
#define KPKDI 0x0048
/* bit definitions */
#define KPC_MKRN(n) ((((n) - 1) & 0x7) << 26) /* matrix key row number */
#define KPC_MKCN(n) ((((n) - 1) & 0x7) << 23) /* matrix key column number */
#define KPC_DKN(n) ((((n) - 1) & 0x7) << 6) /* direct key number */
#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
#define KPC_MS(n) (0x1 << (13 + (n))) /* Matrix scan line 'n' */
#define KPC_MS_ALL (0xff << 13)
#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
#define KPDK_DKP (0x1 << 31)
#define KPDK_DK(n) ((n) & 0xff)
#define KPREC_OF1 (0x1 << 31)
#define kPREC_UF1 (0x1 << 30)
#define KPREC_OF0 (0x1 << 15)
#define KPREC_UF0 (0x1 << 14)
#define KPREC_RECOUNT0(n) ((n) & 0xff)
#define KPREC_RECOUNT1(n) (((n) >> 16) & 0xff)
#define KPMK_MKP (0x1 << 31)
#define KPAS_SO (0x1 << 31)
#define KPASMKPx_SO (0x1 << 31)
#define KPAS_MUKP(n) (((n) >> 26) & 0x1f)
#define KPAS_RP(n) (((n) >> 4) & 0xf)
#define KPAS_CP(n) ((n) & 0xf)
#define KPASMKP_MKC_MASK (0xff)
#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off))
#define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off))
#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
#define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM)
struct pxa27x_keypad {
struct pxa27x_keypad_platform_data *pdata;
struct clk *clk;
struct input_dev *input_dev;
void __iomem *mmio_base;
int irq;
unsigned short keycodes[MAX_KEYPAD_KEYS];
int rotary_rel_code[2];
/* state row bits of each column scan */
uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
uint32_t direct_key_state;
unsigned int direct_key_mask;
};
static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
unsigned short keycode;
int i;
for (i = 0; i < pdata->matrix_key_map_size; i++) {
unsigned int key = pdata->matrix_key_map[i];
unsigned int row = KEY_ROW(key);
unsigned int col = KEY_COL(key);
unsigned int scancode = MATRIX_SCAN_CODE(row, col,
MATRIX_ROW_SHIFT);
keycode = KEY_VAL(key);
keypad->keycodes[scancode] = keycode;
__set_bit(keycode, input_dev->keybit);
}
for (i = 0; i < pdata->direct_key_num; i++) {
keycode = pdata->direct_key_map[i];
keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode;
__set_bit(keycode, input_dev->keybit);
}
if (pdata->enable_rotary0) {
if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
keycode = pdata->rotary0_up_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode;
__set_bit(keycode, input_dev->keybit);
keycode = pdata->rotary0_down_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode;
__set_bit(keycode, input_dev->keybit);
keypad->rotary_rel_code[0] = -1;
} else {
keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
__set_bit(pdata->rotary0_rel_code, input_dev->relbit);
}
}
if (pdata->enable_rotary1) {
if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
keycode = pdata->rotary1_up_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode;
__set_bit(keycode, input_dev->keybit);
keycode = pdata->rotary1_down_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode;
__set_bit(keycode, input_dev->keybit);
keypad->rotary_rel_code[1] = -1;
} else {
keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
__set_bit(pdata->rotary1_rel_code, input_dev->relbit);
}
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
}
static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
int row, col, num_keys_pressed = 0;
uint32_t new_state[MAX_MATRIX_KEY_COLS];
uint32_t kpas = keypad_readl(KPAS);
num_keys_pressed = KPAS_MUKP(kpas);
memset(new_state, 0, sizeof(new_state));
if (num_keys_pressed == 0)
goto scan;
if (num_keys_pressed == 1) {
col = KPAS_CP(kpas);
row = KPAS_RP(kpas);
/* if invalid row/col, treat as no key pressed */
if (col >= pdata->matrix_key_cols ||
row >= pdata->matrix_key_rows)
goto scan;
new_state[col] = (1 << row);
goto scan;
}
if (num_keys_pressed > 1) {
uint32_t kpasmkp0 = keypad_readl(KPASMKP0);
uint32_t kpasmkp1 = keypad_readl(KPASMKP1);
uint32_t kpasmkp2 = keypad_readl(KPASMKP2);
uint32_t kpasmkp3 = keypad_readl(KPASMKP3);
new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK;
new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK;
new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK;
new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK;
new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK;
new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK;
new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK;
new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK;
}
scan:
for (col = 0; col < pdata->matrix_key_cols; col++) {
uint32_t bits_changed;
int code;
bits_changed = keypad->matrix_key_state[col] ^ new_state[col];
if (bits_changed == 0)
continue;
for (row = 0; row < pdata->matrix_key_rows; row++) {
if ((bits_changed & (1 << row)) == 0)
continue;
code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, keypad->keycodes[code],
new_state[col] & (1 << row));
}
}
input_sync(input_dev);
memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));
}
#define DEFAULT_KPREC (0x007f007f)
static inline int rotary_delta(uint32_t kprec)
{
if (kprec & KPREC_OF0)
return (kprec & 0xff) + 0x7f;
else if (kprec & KPREC_UF0)
return (kprec & 0xff) - 0x7f - 0xff;
else
return (kprec & 0xff) - 0x7f;
}
static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
{
struct input_dev *dev = keypad->input_dev;
if (delta == 0)
return;
if (keypad->rotary_rel_code[r] == -1) {
int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1);
unsigned char keycode = keypad->keycodes[code];
/* simulate a press-n-release */
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 1);
input_sync(dev);
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 0);
input_sync(dev);
} else {
input_report_rel(dev, keypad->rotary_rel_code[r], delta);
input_sync(dev);
}
}
static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
uint32_t kprec;
/* read and reset to default count value */
kprec = keypad_readl(KPREC);
keypad_writel(KPREC, DEFAULT_KPREC);
if (pdata->enable_rotary0)
report_rotary_event(keypad, 0, rotary_delta(kprec));
if (pdata->enable_rotary1)
report_rotary_event(keypad, 1, rotary_delta(kprec >> 16));
}
static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
unsigned int new_state;
uint32_t kpdk, bits_changed;
int i;
kpdk = keypad_readl(KPDK);
if (pdata->enable_rotary0 || pdata->enable_rotary1)
pxa27x_keypad_scan_rotary(keypad);
/*
* The KPDR_DK only output the key pin level, so it relates to board,
* and low level may be active.
*/
if (pdata->direct_key_low_active)
new_state = ~KPDK_DK(kpdk) & keypad->direct_key_mask;
else
new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
bits_changed = keypad->direct_key_state ^ new_state;
if (bits_changed == 0)
return;
for (i = 0; i < pdata->direct_key_num; i++) {
if (bits_changed & (1 << i)) {
int code = MAX_MATRIX_KEY_NUM + i;
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, keypad->keycodes[code],
new_state & (1 << i));
}
}
input_sync(input_dev);
keypad->direct_key_state = new_state;
}
static void clear_wakeup_event(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
if (pdata->clear_wakeup_event)
(pdata->clear_wakeup_event)();
}
static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
{
struct pxa27x_keypad *keypad = dev_id;
unsigned long kpc = keypad_readl(KPC);
clear_wakeup_event(keypad);
if (kpc & KPC_DI)
pxa27x_keypad_scan_direct(keypad);
if (kpc & KPC_MI)
pxa27x_keypad_scan_matrix(keypad);
return IRQ_HANDLED;
}
static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
unsigned int mask = 0, direct_key_num = 0;
unsigned long kpc = 0;
/* clear pending interrupt bit */
keypad_readl(KPC);
/* enable matrix keys with automatic scan */
if (pdata->matrix_key_rows && pdata->matrix_key_cols) {
kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL;
kpc |= KPC_MKRN(pdata->matrix_key_rows) |
KPC_MKCN(pdata->matrix_key_cols);
}
/* enable rotary key, debounce interval same as direct keys */
if (pdata->enable_rotary0) {
mask |= 0x03;
direct_key_num = 2;
kpc |= KPC_REE0;
}
if (pdata->enable_rotary1) {
mask |= 0x0c;
direct_key_num = 4;
kpc |= KPC_REE1;
}
if (pdata->direct_key_num > direct_key_num)
direct_key_num = pdata->direct_key_num;
/*
* Direct keys usage may not start from KP_DKIN0, check the platfrom
* mask data to config the specific.
*/
if (pdata->direct_key_mask)
keypad->direct_key_mask = pdata->direct_key_mask;
else
keypad->direct_key_mask = ((1 << direct_key_num) - 1) & ~mask;
/* enable direct key */
if (direct_key_num)
kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num);
keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB);
keypad_writel(KPREC, DEFAULT_KPREC);
keypad_writel(KPKDI, pdata->debounce_interval);
}
static int pxa27x_keypad_open(struct input_dev *dev)
{
struct pxa27x_keypad *keypad = input_get_drvdata(dev);
/* Enable unit clock */
clk_prepare_enable(keypad->clk);
pxa27x_keypad_config(keypad);
return 0;
}
static void pxa27x_keypad_close(struct input_dev *dev)
{
struct pxa27x_keypad *keypad = input_get_drvdata(dev);
/* Disable clock unit */
clk_disable_unprepare(keypad->clk);
}
#ifdef CONFIG_PM
static int pxa27x_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
/*
* If the keypad is used a wake up source, clock can not be disabled.
* Or it can not detect the key pressing.
*/
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(keypad->irq);
else
clk_disable_unprepare(keypad->clk);
return 0;
}
static int pxa27x_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
/*
* If the keypad is used as wake up source, the clock is not turned
* off. So do not need configure it again.
*/
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(keypad->irq);
} else {
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
/* Enable unit clock */
clk_prepare_enable(keypad->clk);
pxa27x_keypad_config(keypad);
}
mutex_unlock(&input_dev->mutex);
}
return 0;
}
static const struct dev_pm_ops pxa27x_keypad_pm_ops = {
.suspend = pxa27x_keypad_suspend,
.resume = pxa27x_keypad_resume,
};
#endif
static int pxa27x_keypad_probe(struct platform_device *pdev)
{
struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
struct pxa27x_keypad *keypad;
struct input_dev *input_dev;
struct resource *res;
int irq, error;
if (pdata == NULL) {
dev_err(&pdev->dev, "no platform data defined\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get keypad irq\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
return -ENXIO;
}
keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL);
input_dev = input_allocate_device();
if (!keypad || !input_dev) {
dev_err(&pdev->dev, "failed to allocate memory\n");
error = -ENOMEM;
goto failed_free;
}
keypad->pdata = pdata;
keypad->input_dev = input_dev;
keypad->irq = irq;
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (res == NULL) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto failed_free;
}
keypad->mmio_base = ioremap(res->start, resource_size(res));
if (keypad->mmio_base == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -ENXIO;
goto failed_free_mem;
}
keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
error = PTR_ERR(keypad->clk);
goto failed_free_io;
}
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = pxa27x_keypad_open;
input_dev->close = pxa27x_keypad_close;
input_dev->dev.parent = &pdev->dev;
input_dev->keycode = keypad->keycodes;
input_dev->keycodesize = sizeof(keypad->keycodes[0]);
input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
input_set_drvdata(input_dev, keypad);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
pxa27x_keypad_build_keycode(keypad);
if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) ||
(pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) {
input_dev->evbit[0] |= BIT_MASK(EV_REL);
}
error = request_irq(irq, pxa27x_keypad_irq_handler, 0,
pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto failed_put_clk;
}
/* Register the input device */
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
goto failed_free_irq;
}
platform_set_drvdata(pdev, keypad);
device_init_wakeup(&pdev->dev, 1);
return 0;
failed_free_irq:
free_irq(irq, pdev);
failed_put_clk:
clk_put(keypad->clk);
failed_free_io:
iounmap(keypad->mmio_base);
failed_free_mem:
release_mem_region(res->start, resource_size(res));
failed_free:
input_free_device(input_dev);
kfree(keypad);
return error;
}
static int pxa27x_keypad_remove(struct platform_device *pdev)
{
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
struct resource *res;
free_irq(keypad->irq, pdev);
clk_put(keypad->clk);
input_unregister_device(keypad->input_dev);
iounmap(keypad->mmio_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
kfree(keypad);
return 0;
}
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:pxa27x-keypad");
static struct platform_driver pxa27x_keypad_driver = {
.probe = pxa27x_keypad_probe,
.remove = pxa27x_keypad_remove,
.driver = {
.name = "pxa27x-keypad",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &pxa27x_keypad_pm_ops,
#endif
},
};
module_platform_driver(pxa27x_keypad_driver);
MODULE_DESCRIPTION("PXA27x Keypad Controller Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,202 @@
/*
* Driver for the enhanced rotary controller on pxa930 and pxa935
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/platform_data/keyboard-pxa930_rotary.h>
#define SBCR (0x04)
#define ERCR (0x0c)
#define SBCR_ERSB (1 << 5)
struct pxa930_rotary {
struct input_dev *input_dev;
void __iomem *mmio_base;
int last_ercr;
struct pxa930_rotary_platform_data *pdata;
};
static void clear_sbcr(struct pxa930_rotary *r)
{
uint32_t sbcr = __raw_readl(r->mmio_base + SBCR);
__raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR);
__raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR);
}
static irqreturn_t rotary_irq(int irq, void *dev_id)
{
struct pxa930_rotary *r = dev_id;
struct pxa930_rotary_platform_data *pdata = r->pdata;
int ercr, delta, key;
ercr = __raw_readl(r->mmio_base + ERCR) & 0xf;
clear_sbcr(r);
delta = ercr - r->last_ercr;
if (delta == 0)
return IRQ_HANDLED;
r->last_ercr = ercr;
if (pdata->up_key && pdata->down_key) {
key = (delta > 0) ? pdata->up_key : pdata->down_key;
input_report_key(r->input_dev, key, 1);
input_sync(r->input_dev);
input_report_key(r->input_dev, key, 0);
} else
input_report_rel(r->input_dev, pdata->rel_code, delta);
input_sync(r->input_dev);
return IRQ_HANDLED;
}
static int pxa930_rotary_open(struct input_dev *dev)
{
struct pxa930_rotary *r = input_get_drvdata(dev);
clear_sbcr(r);
return 0;
}
static void pxa930_rotary_close(struct input_dev *dev)
{
struct pxa930_rotary *r = input_get_drvdata(dev);
clear_sbcr(r);
}
static int pxa930_rotary_probe(struct platform_device *pdev)
{
struct pxa930_rotary_platform_data *pdata = pdev->dev.platform_data;
struct pxa930_rotary *r;
struct input_dev *input_dev;
struct resource *res;
int irq;
int err;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for rotary controller\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no I/O memory defined\n");
return -ENXIO;
}
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
return -EINVAL;
}
r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL);
if (!r)
return -ENOMEM;
r->mmio_base = ioremap_nocache(res->start, resource_size(res));
if (r->mmio_base == NULL) {
dev_err(&pdev->dev, "failed to remap IO memory\n");
err = -ENXIO;
goto failed_free;
}
r->pdata = pdata;
platform_set_drvdata(pdev, r);
/* allocate and register the input device */
input_dev = input_allocate_device();
if (!input_dev) {
dev_err(&pdev->dev, "failed to allocate input device\n");
err = -ENOMEM;
goto failed_free_io;
}
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = pxa930_rotary_open;
input_dev->close = pxa930_rotary_close;
input_dev->dev.parent = &pdev->dev;
if (pdata->up_key && pdata->down_key) {
__set_bit(pdata->up_key, input_dev->keybit);
__set_bit(pdata->down_key, input_dev->keybit);
__set_bit(EV_KEY, input_dev->evbit);
} else {
__set_bit(pdata->rel_code, input_dev->relbit);
__set_bit(EV_REL, input_dev->evbit);
}
r->input_dev = input_dev;
input_set_drvdata(input_dev, r);
err = request_irq(irq, rotary_irq, 0,
"enhanced rotary", r);
if (err) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto failed_free_input;
}
err = input_register_device(input_dev);
if (err) {
dev_err(&pdev->dev, "failed to register input device\n");
goto failed_free_irq;
}
return 0;
failed_free_irq:
free_irq(irq, r);
failed_free_input:
input_free_device(input_dev);
failed_free_io:
iounmap(r->mmio_base);
failed_free:
kfree(r);
return err;
}
static int pxa930_rotary_remove(struct platform_device *pdev)
{
struct pxa930_rotary *r = platform_get_drvdata(pdev);
free_irq(platform_get_irq(pdev, 0), r);
input_unregister_device(r->input_dev);
iounmap(r->mmio_base);
platform_set_drvdata(pdev, NULL);
kfree(r);
return 0;
}
static struct platform_driver pxa930_rotary_driver = {
.driver = {
.name = "pxa930-rotary",
.owner = THIS_MODULE,
},
.probe = pxa930_rotary_probe,
.remove = pxa930_rotary_remove,
};
module_platform_driver(pxa930_rotary_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller");
MODULE_AUTHOR("Yao Yong <yaoyong@marvell.com>");

View File

@@ -0,0 +1,266 @@
/*
* Atmel AT42QT1070 QTouch Sensor Controller
*
* Copyright (C) 2011 Atmel
*
* Authors: Bo Shen <voice.shen@atmel.com>
*
* Base on AT42QT2160 driver by:
* Raphael Derosso Pereira <raphaelpereira@gmail.com>
* Copyright (C) 2009
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
/* Address for each register */
#define CHIP_ID 0x00
#define QT1070_CHIP_ID 0x2E
#define FW_VERSION 0x01
#define QT1070_FW_VERSION 0x15
#define DET_STATUS 0x02
#define KEY_STATUS 0x03
/* Calibrate */
#define CALIBRATE_CMD 0x38
#define QT1070_CAL_TIME 200
/* Reset */
#define RESET 0x39
#define QT1070_RESET_TIME 255
/* AT42QT1070 support up to 7 keys */
static const unsigned short qt1070_key2code[] = {
KEY_0, KEY_1, KEY_2, KEY_3,
KEY_4, KEY_5, KEY_6,
};
struct qt1070_data {
struct i2c_client *client;
struct input_dev *input;
unsigned int irq;
unsigned short keycodes[ARRAY_SIZE(qt1070_key2code)];
u8 last_keys;
};
static int qt1070_read(struct i2c_client *client, u8 reg)
{
int ret;
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
dev_err(&client->dev,
"can not read register, returned %d\n", ret);
return ret;
}
static int qt1070_write(struct i2c_client *client, u8 reg, u8 data)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, data);
if (ret < 0)
dev_err(&client->dev,
"can not write register, returned %d\n", ret);
return ret;
}
static bool qt1070_identify(struct i2c_client *client)
{
int id, ver;
/* Read Chip ID */
id = qt1070_read(client, CHIP_ID);
if (id != QT1070_CHIP_ID) {
dev_err(&client->dev, "ID %d not supported\n", id);
return false;
}
/* Read firmware version */
ver = qt1070_read(client, FW_VERSION);
if (ver < 0) {
dev_err(&client->dev, "could not read the firmware version\n");
return false;
}
dev_info(&client->dev, "AT42QT1070 firmware version %x\n", ver);
return true;
}
static irqreturn_t qt1070_interrupt(int irq, void *dev_id)
{
struct qt1070_data *data = dev_id;
struct i2c_client *client = data->client;
struct input_dev *input = data->input;
int i;
u8 new_keys, keyval, mask = 0x01;
/* Read the detected status register, thus clearing interrupt */
qt1070_read(client, DET_STATUS);
/* Read which key changed */
new_keys = qt1070_read(client, KEY_STATUS);
for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) {
keyval = new_keys & mask;
if ((data->last_keys & mask) != keyval)
input_report_key(input, data->keycodes[i], keyval);
mask <<= 1;
}
input_sync(input);
data->last_keys = new_keys;
return IRQ_HANDLED;
}
static int qt1070_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct qt1070_data *data;
struct input_dev *input;
int i;
int err;
err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
if (!err) {
dev_err(&client->dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
if (!client->irq) {
dev_err(&client->dev, "please assign the irq to this device\n");
return -EINVAL;
}
/* Identify the qt1070 chip */
if (!qt1070_identify(client))
return -ENODEV;
data = kzalloc(sizeof(struct qt1070_data), GFP_KERNEL);
input = input_allocate_device();
if (!data || !input) {
dev_err(&client->dev, "insufficient memory\n");
err = -ENOMEM;
goto err_free_mem;
}
data->client = client;
data->input = input;
data->irq = client->irq;
input->name = "AT42QT1070 QTouch Sensor";
input->dev.parent = &client->dev;
input->id.bustype = BUS_I2C;
/* Add the keycode */
input->keycode = data->keycodes;
input->keycodesize = sizeof(data->keycodes[0]);
input->keycodemax = ARRAY_SIZE(qt1070_key2code);
__set_bit(EV_KEY, input->evbit);
for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) {
data->keycodes[i] = qt1070_key2code[i];
__set_bit(qt1070_key2code[i], input->keybit);
}
/* Calibrate device */
qt1070_write(client, CALIBRATE_CMD, 1);
msleep(QT1070_CAL_TIME);
/* Soft reset */
qt1070_write(client, RESET, 1);
msleep(QT1070_RESET_TIME);
err = request_threaded_irq(client->irq, NULL, qt1070_interrupt,
IRQF_TRIGGER_NONE | IRQF_ONESHOT,
client->dev.driver->name, data);
if (err) {
dev_err(&client->dev, "fail to request irq\n");
goto err_free_mem;
}
/* Register the input device */
err = input_register_device(data->input);
if (err) {
dev_err(&client->dev, "Failed to register input device\n");
goto err_free_irq;
}
i2c_set_clientdata(client, data);
/* Read to clear the chang line */
qt1070_read(client, DET_STATUS);
return 0;
err_free_irq:
free_irq(client->irq, data);
err_free_mem:
input_free_device(input);
kfree(data);
return err;
}
static int qt1070_remove(struct i2c_client *client)
{
struct qt1070_data *data = i2c_get_clientdata(client);
/* Release IRQ */
free_irq(client->irq, data);
input_unregister_device(data->input);
kfree(data);
return 0;
}
static const struct i2c_device_id qt1070_id[] = {
{ "qt1070", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, qt1070_id);
static struct i2c_driver qt1070_driver = {
.driver = {
.name = "qt1070",
.owner = THIS_MODULE,
},
.id_table = qt1070_id,
.probe = qt1070_probe,
.remove = qt1070_remove,
};
module_i2c_driver(qt1070_driver);
MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,513 @@
/*
* qt2160.c - Atmel AT42QT2160 Touch Sense Controller
*
* Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#define QT2160_VALID_CHIPID 0x11
#define QT2160_CMD_CHIPID 0
#define QT2160_CMD_CODEVER 1
#define QT2160_CMD_GSTAT 2
#define QT2160_CMD_KEYS3 3
#define QT2160_CMD_KEYS4 4
#define QT2160_CMD_SLIDE 5
#define QT2160_CMD_GPIOS 6
#define QT2160_CMD_SUBVER 7
#define QT2160_CMD_CALIBRATE 10
#define QT2160_CMD_DRIVE_X 70
#define QT2160_CMD_PWMEN_X 74
#define QT2160_CMD_PWM_DUTY 76
#define QT2160_NUM_LEDS_X 8
#define QT2160_CYCLE_INTERVAL (2*HZ)
static unsigned char qt2160_key2code[] = {
KEY_0, KEY_1, KEY_2, KEY_3,
KEY_4, KEY_5, KEY_6, KEY_7,
KEY_8, KEY_9, KEY_A, KEY_B,
KEY_C, KEY_D, KEY_E, KEY_F,
};
#ifdef CONFIG_LEDS_CLASS
struct qt2160_led {
struct qt2160_data *qt2160;
struct led_classdev cdev;
struct work_struct work;
char name[32];
int id;
enum led_brightness new_brightness;
};
#endif
struct qt2160_data {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
spinlock_t lock; /* Protects canceling/rescheduling of dwork */
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
u16 key_matrix;
#ifdef CONFIG_LEDS_CLASS
struct qt2160_led leds[QT2160_NUM_LEDS_X];
struct mutex led_lock;
#endif
};
static int qt2160_read(struct i2c_client *client, u8 reg);
static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
#ifdef CONFIG_LEDS_CLASS
static void qt2160_led_work(struct work_struct *work)
{
struct qt2160_led *led = container_of(work, struct qt2160_led, work);
struct qt2160_data *qt2160 = led->qt2160;
struct i2c_client *client = qt2160->client;
int value = led->new_brightness;
u32 drive, pwmen;
mutex_lock(&qt2160->led_lock);
drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
if (value != LED_OFF) {
drive |= (1 << led->id);
pwmen |= (1 << led->id);
} else {
drive &= ~(1 << led->id);
pwmen &= ~(1 << led->id);
}
qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
/*
* Changing this register will change the brightness
* of every LED in the qt2160. It's a HW limitation.
*/
if (value != LED_OFF)
qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
mutex_unlock(&qt2160->led_lock);
}
static void qt2160_led_set(struct led_classdev *cdev,
enum led_brightness value)
{
struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
}
#endif /* CONFIG_LEDS_CLASS */
static int qt2160_read_block(struct i2c_client *client,
u8 inireg, u8 *buffer, unsigned int count)
{
int error, idx = 0;
/*
* Can't use SMBus block data read. Check for I2C functionality to speed
* things up whenever possible. Otherwise we will be forced to read
* sequentially.
*/
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
error = i2c_smbus_write_byte(client, inireg + idx);
if (error) {
dev_err(&client->dev,
"couldn't send request. Returned %d\n", error);
return error;
}
error = i2c_master_recv(client, buffer, count);
if (error != count) {
dev_err(&client->dev,
"couldn't read registers. Returned %d bytes\n", error);
return error;
}
} else {
while (count--) {
int data;
error = i2c_smbus_write_byte(client, inireg + idx);
if (error) {
dev_err(&client->dev,
"couldn't send request. Returned %d\n", error);
return error;
}
data = i2c_smbus_read_byte(client);
if (data < 0) {
dev_err(&client->dev,
"couldn't read register. Returned %d\n", data);
return data;
}
buffer[idx++] = data;
}
}
return 0;
}
static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
{
struct i2c_client *client = qt2160->client;
struct input_dev *input = qt2160->input;
u8 regs[6];
u16 old_matrix, new_matrix;
int ret, i, mask;
dev_dbg(&client->dev, "requesting keys...\n");
/*
* Read all registers from General Status Register
* to GPIOs register
*/
ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6);
if (ret) {
dev_err(&client->dev,
"could not perform chip read.\n");
return ret;
}
old_matrix = qt2160->key_matrix;
qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1];
mask = 0x01;
for (i = 0; i < 16; ++i, mask <<= 1) {
int keyval = new_matrix & mask;
if ((old_matrix & mask) != keyval) {
input_report_key(input, qt2160->keycodes[i], keyval);
dev_dbg(&client->dev, "key %d %s\n",
i, keyval ? "pressed" : "released");
}
}
input_sync(input);
return 0;
}
static irqreturn_t qt2160_irq(int irq, void *_qt2160)
{
struct qt2160_data *qt2160 = _qt2160;
unsigned long flags;
spin_lock_irqsave(&qt2160->lock, flags);
mod_delayed_work(system_wq, &qt2160->dwork, 0);
spin_unlock_irqrestore(&qt2160->lock, flags);
return IRQ_HANDLED;
}
static void qt2160_schedule_read(struct qt2160_data *qt2160)
{
spin_lock_irq(&qt2160->lock);
schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
spin_unlock_irq(&qt2160->lock);
}
static void qt2160_worker(struct work_struct *work)
{
struct qt2160_data *qt2160 =
container_of(work, struct qt2160_data, dwork.work);
dev_dbg(&qt2160->client->dev, "worker\n");
qt2160_get_key_matrix(qt2160);
/* Avoid device lock up by checking every so often */
qt2160_schedule_read(qt2160);
}
static int qt2160_read(struct i2c_client *client, u8 reg)
{
int ret;
ret = i2c_smbus_write_byte(client, reg);
if (ret) {
dev_err(&client->dev,
"couldn't send request. Returned %d\n", ret);
return ret;
}
ret = i2c_smbus_read_byte(client);
if (ret < 0) {
dev_err(&client->dev,
"couldn't read register. Returned %d\n", ret);
return ret;
}
return ret;
}
static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, data);
if (ret < 0)
dev_err(&client->dev,
"couldn't write data. Returned %d\n", ret);
return ret;
}
#ifdef CONFIG_LEDS_CLASS
static int qt2160_register_leds(struct qt2160_data *qt2160)
{
struct i2c_client *client = qt2160->client;
int ret;
int i;
mutex_init(&qt2160->led_lock);
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
struct qt2160_led *led = &qt2160->leds[i];
snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
led->cdev.name = led->name;
led->cdev.brightness_set = qt2160_led_set;
led->cdev.brightness = LED_OFF;
led->id = i;
led->qt2160 = qt2160;
INIT_WORK(&led->work, qt2160_led_work);
ret = led_classdev_register(&client->dev, &led->cdev);
if (ret < 0)
return ret;
}
/* Tur off LEDs */
qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
return 0;
}
static void qt2160_unregister_leds(struct qt2160_data *qt2160)
{
int i;
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
led_classdev_unregister(&qt2160->leds[i].cdev);
cancel_work_sync(&qt2160->leds[i].work);
}
}
#else
static inline int qt2160_register_leds(struct qt2160_data *qt2160)
{
return 0;
}
static inline void qt2160_unregister_leds(struct qt2160_data *qt2160)
{
}
#endif
static bool qt2160_identify(struct i2c_client *client)
{
int id, ver, rev;
/* Read Chid ID to check if chip is valid */
id = qt2160_read(client, QT2160_CMD_CHIPID);
if (id != QT2160_VALID_CHIPID) {
dev_err(&client->dev, "ID %d not supported\n", id);
return false;
}
/* Read chip firmware version */
ver = qt2160_read(client, QT2160_CMD_CODEVER);
if (ver < 0) {
dev_err(&client->dev, "could not get firmware version\n");
return false;
}
/* Read chip firmware revision */
rev = qt2160_read(client, QT2160_CMD_SUBVER);
if (rev < 0) {
dev_err(&client->dev, "could not get firmware revision\n");
return false;
}
dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n",
ver >> 4, ver & 0xf, rev);
return true;
}
static int qt2160_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct qt2160_data *qt2160;
struct input_dev *input;
int i;
int error;
/* Check functionality */
error = i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE);
if (!error) {
dev_err(&client->dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
if (!qt2160_identify(client))
return -ENODEV;
/* Chip is valid and active. Allocate structure */
qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL);
input = input_allocate_device();
if (!qt2160 || !input) {
dev_err(&client->dev, "insufficient memory\n");
error = -ENOMEM;
goto err_free_mem;
}
qt2160->client = client;
qt2160->input = input;
INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
spin_lock_init(&qt2160->lock);
input->name = "AT42QT2160 Touch Sense Keyboard";
input->id.bustype = BUS_I2C;
input->keycode = qt2160->keycodes;
input->keycodesize = sizeof(qt2160->keycodes[0]);
input->keycodemax = ARRAY_SIZE(qt2160_key2code);
__set_bit(EV_KEY, input->evbit);
__clear_bit(EV_REP, input->evbit);
for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) {
qt2160->keycodes[i] = qt2160_key2code[i];
__set_bit(qt2160_key2code[i], input->keybit);
}
__clear_bit(KEY_RESERVED, input->keybit);
/* Calibrate device */
error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1);
if (error) {
dev_err(&client->dev, "failed to calibrate device\n");
goto err_free_mem;
}
if (client->irq) {
error = request_irq(client->irq, qt2160_irq,
IRQF_TRIGGER_FALLING, "qt2160", qt2160);
if (error) {
dev_err(&client->dev,
"failed to allocate irq %d\n", client->irq);
goto err_free_mem;
}
}
error = qt2160_register_leds(qt2160);
if (error) {
dev_err(&client->dev, "Failed to register leds\n");
goto err_free_irq;
}
error = input_register_device(qt2160->input);
if (error) {
dev_err(&client->dev,
"Failed to register input device\n");
goto err_unregister_leds;
}
i2c_set_clientdata(client, qt2160);
qt2160_schedule_read(qt2160);
return 0;
err_unregister_leds:
qt2160_unregister_leds(qt2160);
err_free_irq:
if (client->irq)
free_irq(client->irq, qt2160);
err_free_mem:
input_free_device(input);
kfree(qt2160);
return error;
}
static int qt2160_remove(struct i2c_client *client)
{
struct qt2160_data *qt2160 = i2c_get_clientdata(client);
qt2160_unregister_leds(qt2160);
/* Release IRQ so no queue will be scheduled */
if (client->irq)
free_irq(client->irq, qt2160);
cancel_delayed_work_sync(&qt2160->dwork);
input_unregister_device(qt2160->input);
kfree(qt2160);
return 0;
}
static const struct i2c_device_id qt2160_idtable[] = {
{ "qt2160", 0, },
{ }
};
MODULE_DEVICE_TABLE(i2c, qt2160_idtable);
static struct i2c_driver qt2160_driver = {
.driver = {
.name = "qt2160",
.owner = THIS_MODULE,
},
.id_table = qt2160_idtable,
.probe = qt2160_probe,
.remove = qt2160_remove,
};
module_i2c_driver(qt2160_driver);
MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>");
MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,661 @@
/*
* Samsung keypad driver
*
* Copyright (C) 2010 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
* Author: Donghwa Lee <dh09.lee@samsung.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.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/sched.h>
#include <linux/input/samsung-keypad.h>
#define SAMSUNG_KEYIFCON 0x00
#define SAMSUNG_KEYIFSTSCLR 0x04
#define SAMSUNG_KEYIFCOL 0x08
#define SAMSUNG_KEYIFROW 0x0c
#define SAMSUNG_KEYIFFC 0x10
/* SAMSUNG_KEYIFCON */
#define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0)
#define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1)
#define SAMSUNG_KEYIFCON_DF_EN (1 << 2)
#define SAMSUNG_KEYIFCON_FC_EN (1 << 3)
#define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4)
/* SAMSUNG_KEYIFSTSCLR */
#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0)
#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK (0xff << 8)
#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET 8
#define S5PV210_KEYIFSTSCLR_P_INT_MASK (0x3fff << 0)
#define S5PV210_KEYIFSTSCLR_R_INT_MASK (0x3fff << 16)
#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16
/* SAMSUNG_KEYIFCOL */
#define SAMSUNG_KEYIFCOL_MASK (0xff << 0)
#define S5PV210_KEYIFCOLEN_MASK (0xff << 8)
/* SAMSUNG_KEYIFROW */
#define SAMSUNG_KEYIFROW_MASK (0xff << 0)
#define S5PV210_KEYIFROW_MASK (0x3fff << 0)
/* SAMSUNG_KEYIFFC */
#define SAMSUNG_KEYIFFC_MASK (0x3ff << 0)
enum samsung_keypad_type {
KEYPAD_TYPE_SAMSUNG,
KEYPAD_TYPE_S5PV210,
};
struct samsung_keypad {
struct input_dev *input_dev;
struct platform_device *pdev;
struct clk *clk;
void __iomem *base;
wait_queue_head_t wait;
bool stopped;
bool wake_enabled;
int irq;
enum samsung_keypad_type type;
unsigned int row_shift;
unsigned int rows;
unsigned int cols;
unsigned int row_state[SAMSUNG_MAX_COLS];
#ifdef CONFIG_OF
int row_gpios[SAMSUNG_MAX_ROWS];
int col_gpios[SAMSUNG_MAX_COLS];
#endif
unsigned short keycodes[];
};
static void samsung_keypad_scan(struct samsung_keypad *keypad,
unsigned int *row_state)
{
unsigned int col;
unsigned int val;
for (col = 0; col < keypad->cols; col++) {
if (keypad->type == KEYPAD_TYPE_S5PV210) {
val = S5PV210_KEYIFCOLEN_MASK;
val &= ~(1 << col) << 8;
} else {
val = SAMSUNG_KEYIFCOL_MASK;
val &= ~(1 << col);
}
writel(val, keypad->base + SAMSUNG_KEYIFCOL);
mdelay(1);
val = readl(keypad->base + SAMSUNG_KEYIFROW);
row_state[col] = ~val & ((1 << keypad->rows) - 1);
}
/* KEYIFCOL reg clear */
writel(0, keypad->base + SAMSUNG_KEYIFCOL);
}
static bool samsung_keypad_report(struct samsung_keypad *keypad,
unsigned int *row_state)
{
struct input_dev *input_dev = keypad->input_dev;
unsigned int changed;
unsigned int pressed;
unsigned int key_down = 0;
unsigned int val;
unsigned int col, row;
for (col = 0; col < keypad->cols; col++) {
changed = row_state[col] ^ keypad->row_state[col];
key_down |= row_state[col];
if (!changed)
continue;
for (row = 0; row < keypad->rows; row++) {
if (!(changed & (1 << row)))
continue;
pressed = row_state[col] & (1 << row);
dev_dbg(&keypad->input_dev->dev,
"key %s, row: %d, col: %d\n",
pressed ? "pressed" : "released", row, col);
val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(input_dev, EV_MSC, MSC_SCAN, val);
input_report_key(input_dev,
keypad->keycodes[val], pressed);
}
input_sync(keypad->input_dev);
}
memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
return key_down;
}
static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
{
struct samsung_keypad *keypad = dev_id;
unsigned int row_state[SAMSUNG_MAX_COLS];
unsigned int val;
bool key_down;
pm_runtime_get_sync(&keypad->pdev->dev);
do {
val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
/* Clear interrupt. */
writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
samsung_keypad_scan(keypad, row_state);
key_down = samsung_keypad_report(keypad, row_state);
if (key_down)
wait_event_timeout(keypad->wait, keypad->stopped,
msecs_to_jiffies(50));
} while (key_down && !keypad->stopped);
pm_runtime_put(&keypad->pdev->dev);
return IRQ_HANDLED;
}
static void samsung_keypad_start(struct samsung_keypad *keypad)
{
unsigned int val;
pm_runtime_get_sync(&keypad->pdev->dev);
/* Tell IRQ thread that it may poll the device. */
keypad->stopped = false;
clk_enable(keypad->clk);
/* Enable interrupt bits. */
val = readl(keypad->base + SAMSUNG_KEYIFCON);
val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
writel(val, keypad->base + SAMSUNG_KEYIFCON);
/* KEYIFCOL reg clear. */
writel(0, keypad->base + SAMSUNG_KEYIFCOL);
pm_runtime_put(&keypad->pdev->dev);
}
static void samsung_keypad_stop(struct samsung_keypad *keypad)
{
unsigned int val;
pm_runtime_get_sync(&keypad->pdev->dev);
/* Signal IRQ thread to stop polling and disable the handler. */
keypad->stopped = true;
wake_up(&keypad->wait);
disable_irq(keypad->irq);
/* Clear interrupt. */
writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
/* Disable interrupt bits. */
val = readl(keypad->base + SAMSUNG_KEYIFCON);
val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN);
writel(val, keypad->base + SAMSUNG_KEYIFCON);
clk_disable(keypad->clk);
/*
* Now that chip should not generate interrupts we can safely
* re-enable the handler.
*/
enable_irq(keypad->irq);
pm_runtime_put(&keypad->pdev->dev);
}
static int samsung_keypad_open(struct input_dev *input_dev)
{
struct samsung_keypad *keypad = input_get_drvdata(input_dev);
samsung_keypad_start(keypad);
return 0;
}
static void samsung_keypad_close(struct input_dev *input_dev)
{
struct samsung_keypad *keypad = input_get_drvdata(input_dev);
samsung_keypad_stop(keypad);
}
#ifdef CONFIG_OF
static struct samsung_keypad_platdata *samsung_keypad_parse_dt(
struct device *dev)
{
struct samsung_keypad_platdata *pdata;
struct matrix_keymap_data *keymap_data;
uint32_t *keymap, num_rows = 0, num_cols = 0;
struct device_node *np = dev->of_node, *key_np;
unsigned int key_count;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for platform data\n");
return NULL;
}
of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows);
of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols);
if (!num_rows || !num_cols) {
dev_err(dev, "number of keypad rows/columns not specified\n");
return NULL;
}
pdata->rows = num_rows;
pdata->cols = num_cols;
keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL);
if (!keymap_data) {
dev_err(dev, "could not allocate memory for keymap data\n");
return NULL;
}
pdata->keymap_data = keymap_data;
key_count = of_get_child_count(np);
keymap_data->keymap_size = key_count;
keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL);
if (!keymap) {
dev_err(dev, "could not allocate memory for keymap\n");
return NULL;
}
keymap_data->keymap = keymap;
for_each_child_of_node(np, key_np) {
u32 row, col, key_code;
of_property_read_u32(key_np, "keypad,row", &row);
of_property_read_u32(key_np, "keypad,column", &col);
of_property_read_u32(key_np, "linux,code", &key_code);
*keymap++ = KEY(row, col, key_code);
}
if (of_get_property(np, "linux,input-no-autorepeat", NULL))
pdata->no_autorepeat = true;
if (of_get_property(np, "linux,input-wakeup", NULL))
pdata->wakeup = true;
return pdata;
}
static void samsung_keypad_parse_dt_gpio(struct device *dev,
struct samsung_keypad *keypad)
{
struct device_node *np = dev->of_node;
int gpio, error, row, col;
for (row = 0; row < keypad->rows; row++) {
gpio = of_get_named_gpio(np, "row-gpios", row);
keypad->row_gpios[row] = gpio;
if (!gpio_is_valid(gpio)) {
dev_err(dev, "keypad row[%d]: invalid gpio %d\n",
row, gpio);
continue;
}
error = devm_gpio_request(dev, gpio, "keypad-row");
if (error)
dev_err(dev,
"keypad row[%d] gpio request failed: %d\n",
row, error);
}
for (col = 0; col < keypad->cols; col++) {
gpio = of_get_named_gpio(np, "col-gpios", col);
keypad->col_gpios[col] = gpio;
if (!gpio_is_valid(gpio)) {
dev_err(dev, "keypad column[%d]: invalid gpio %d\n",
col, gpio);
continue;
}
error = devm_gpio_request(dev, gpio, "keypad-col");
if (error)
dev_err(dev,
"keypad column[%d] gpio request failed: %d\n",
col, error);
}
}
#else
static
struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev)
{
return NULL;
}
#endif
static int samsung_keypad_probe(struct platform_device *pdev)
{
const struct samsung_keypad_platdata *pdata;
const struct matrix_keymap_data *keymap_data;
struct samsung_keypad *keypad;
struct resource *res;
struct input_dev *input_dev;
unsigned int row_shift;
unsigned int keymap_size;
int error;
if (pdev->dev.of_node)
pdata = samsung_keypad_parse_dt(&pdev->dev);
else
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
return -EINVAL;
}
keymap_data = pdata->keymap_data;
if (!keymap_data) {
dev_err(&pdev->dev, "no keymap data defined\n");
return -EINVAL;
}
if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS)
return -EINVAL;
if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS)
return -EINVAL;
/* initialize the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdata->rows, pdata->cols);
row_shift = get_count_order(pdata->cols);
keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size,
GFP_KERNEL);
input_dev = devm_input_allocate_device(&pdev->dev);
if (!keypad || !input_dev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
keypad->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!keypad->base)
return -EBUSY;
keypad->clk = devm_clk_get(&pdev->dev, "keypad");
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clk\n");
return PTR_ERR(keypad->clk);
}
error = clk_prepare(keypad->clk);
if (error) {
dev_err(&pdev->dev, "keypad clock prepare failed\n");
return error;
}
keypad->input_dev = input_dev;
keypad->pdev = pdev;
keypad->row_shift = row_shift;
keypad->rows = pdata->rows;
keypad->cols = pdata->cols;
keypad->stopped = true;
init_waitqueue_head(&keypad->wait);
if (pdev->dev.of_node) {
#ifdef CONFIG_OF
samsung_keypad_parse_dt_gpio(&pdev->dev, keypad);
keypad->type = of_device_is_compatible(pdev->dev.of_node,
"samsung,s5pv210-keypad");
#endif
} else {
keypad->type = platform_get_device_id(pdev)->driver_data;
}
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
input_dev->open = samsung_keypad_open;
input_dev->close = samsung_keypad_close;
error = matrix_keypad_build_keymap(keymap_data, NULL,
pdata->rows, pdata->cols,
keypad->keycodes, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_unprepare_clk;
}
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
if (!pdata->no_autorepeat)
__set_bit(EV_REP, input_dev->evbit);
input_set_drvdata(input_dev, keypad);
keypad->irq = platform_get_irq(pdev, 0);
if (keypad->irq < 0) {
error = keypad->irq;
goto err_unprepare_clk;
}
error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL,
samsung_keypad_irq, IRQF_ONESHOT,
dev_name(&pdev->dev), keypad);
if (error) {
dev_err(&pdev->dev, "failed to register keypad interrupt\n");
goto err_unprepare_clk;
}
device_init_wakeup(&pdev->dev, pdata->wakeup);
platform_set_drvdata(pdev, keypad);
pm_runtime_enable(&pdev->dev);
error = input_register_device(keypad->input_dev);
if (error)
goto err_disable_runtime_pm;
if (pdev->dev.of_node) {
devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap);
devm_kfree(&pdev->dev, (void *)pdata->keymap_data);
devm_kfree(&pdev->dev, (void *)pdata);
}
return 0;
err_disable_runtime_pm:
pm_runtime_disable(&pdev->dev);
device_init_wakeup(&pdev->dev, 0);
platform_set_drvdata(pdev, NULL);
err_unprepare_clk:
clk_unprepare(keypad->clk);
return error;
}
static int samsung_keypad_remove(struct platform_device *pdev)
{
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
device_init_wakeup(&pdev->dev, 0);
platform_set_drvdata(pdev, NULL);
input_unregister_device(keypad->input_dev);
clk_unprepare(keypad->clk);
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int samsung_keypad_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
unsigned int val;
int error;
if (keypad->stopped)
return 0;
/* This may fail on some SoCs due to lack of controller support */
error = enable_irq_wake(keypad->irq);
if (!error)
keypad->wake_enabled = true;
val = readl(keypad->base + SAMSUNG_KEYIFCON);
val |= SAMSUNG_KEYIFCON_WAKEUPEN;
writel(val, keypad->base + SAMSUNG_KEYIFCON);
clk_disable(keypad->clk);
return 0;
}
static int samsung_keypad_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
unsigned int val;
if (keypad->stopped)
return 0;
clk_enable(keypad->clk);
val = readl(keypad->base + SAMSUNG_KEYIFCON);
val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
writel(val, keypad->base + SAMSUNG_KEYIFCON);
if (keypad->wake_enabled)
disable_irq_wake(keypad->irq);
return 0;
}
#endif
#ifdef CONFIG_PM_SLEEP
static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
bool enable)
{
unsigned int val;
clk_enable(keypad->clk);
val = readl(keypad->base + SAMSUNG_KEYIFCON);
if (enable) {
val |= SAMSUNG_KEYIFCON_WAKEUPEN;
if (device_may_wakeup(&keypad->pdev->dev))
enable_irq_wake(keypad->irq);
} else {
val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
if (device_may_wakeup(&keypad->pdev->dev))
disable_irq_wake(keypad->irq);
}
writel(val, keypad->base + SAMSUNG_KEYIFCON);
clk_disable(keypad->clk);
}
static int samsung_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
mutex_lock(&input_dev->mutex);
if (input_dev->users)
samsung_keypad_stop(keypad);
samsung_keypad_toggle_wakeup(keypad, true);
mutex_unlock(&input_dev->mutex);
return 0;
}
static int samsung_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
mutex_lock(&input_dev->mutex);
samsung_keypad_toggle_wakeup(keypad, false);
if (input_dev->users)
samsung_keypad_start(keypad);
mutex_unlock(&input_dev->mutex);
return 0;
}
#endif
static const struct dev_pm_ops samsung_keypad_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume)
SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend,
samsung_keypad_runtime_resume, NULL)
};
#ifdef CONFIG_OF
static const struct of_device_id samsung_keypad_dt_match[] = {
{ .compatible = "samsung,s3c6410-keypad" },
{ .compatible = "samsung,s5pv210-keypad" },
{},
};
MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
#endif
static struct platform_device_id samsung_keypad_driver_ids[] = {
{
.name = "samsung-keypad",
.driver_data = KEYPAD_TYPE_SAMSUNG,
}, {
.name = "s5pv210-keypad",
.driver_data = KEYPAD_TYPE_S5PV210,
},
{ },
};
MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
static struct platform_driver samsung_keypad_driver = {
.probe = samsung_keypad_probe,
.remove = samsung_keypad_remove,
.driver = {
.name = "samsung-keypad",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_keypad_dt_match),
.pm = &samsung_keypad_pm_ops,
},
.id_table = samsung_keypad_driver_ids,
};
module_platform_driver(samsung_keypad_driver);
MODULE_DESCRIPTION("Samsung keypad driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,344 @@
/*
* SuperH KEYSC Keypad Driver
*
* Copyright (C) 2008 Magnus Damm
*
* Based on gpio_keys.c, Copyright 2005 Phil Blundell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
#include <linux/bitmap.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/slab.h>
static const struct {
unsigned char kymd, keyout, keyin;
} sh_keysc_mode[] = {
[SH_KEYSC_MODE_1] = { 0, 6, 5 },
[SH_KEYSC_MODE_2] = { 1, 5, 6 },
[SH_KEYSC_MODE_3] = { 2, 4, 7 },
[SH_KEYSC_MODE_4] = { 3, 6, 6 },
[SH_KEYSC_MODE_5] = { 4, 6, 7 },
[SH_KEYSC_MODE_6] = { 5, 8, 8 },
};
struct sh_keysc_priv {
void __iomem *iomem_base;
DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS);
struct input_dev *input;
struct sh_keysc_info pdata;
};
#define KYCR1 0
#define KYCR2 1
#define KYINDR 2
#define KYOUTDR 3
#define KYCR2_IRQ_LEVEL 0x10
#define KYCR2_IRQ_DISABLED 0x00
static unsigned long sh_keysc_read(struct sh_keysc_priv *p, int reg_nr)
{
return ioread16(p->iomem_base + (reg_nr << 2));
}
static void sh_keysc_write(struct sh_keysc_priv *p, int reg_nr,
unsigned long value)
{
iowrite16(value, p->iomem_base + (reg_nr << 2));
}
static void sh_keysc_level_mode(struct sh_keysc_priv *p,
unsigned long keys_set)
{
struct sh_keysc_info *pdata = &p->pdata;
sh_keysc_write(p, KYOUTDR, 0);
sh_keysc_write(p, KYCR2, KYCR2_IRQ_LEVEL | (keys_set << 8));
if (pdata->kycr2_delay)
udelay(pdata->kycr2_delay);
}
static void sh_keysc_map_dbg(struct device *dev, unsigned long *map,
const char *str)
{
int k;
for (k = 0; k < BITS_TO_LONGS(SH_KEYSC_MAXKEYS); k++)
dev_dbg(dev, "%s[%d] 0x%lx\n", str, k, map[k]);
}
static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
struct sh_keysc_info *pdata = &priv->pdata;
int keyout_nr = sh_keysc_mode[pdata->mode].keyout;
int keyin_nr = sh_keysc_mode[pdata->mode].keyin;
DECLARE_BITMAP(keys, SH_KEYSC_MAXKEYS);
DECLARE_BITMAP(keys0, SH_KEYSC_MAXKEYS);
DECLARE_BITMAP(keys1, SH_KEYSC_MAXKEYS);
unsigned char keyin_set, tmp;
int i, k, n;
dev_dbg(&pdev->dev, "isr!\n");
bitmap_fill(keys1, SH_KEYSC_MAXKEYS);
bitmap_zero(keys0, SH_KEYSC_MAXKEYS);
do {
bitmap_zero(keys, SH_KEYSC_MAXKEYS);
keyin_set = 0;
sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
for (i = 0; i < keyout_nr; i++) {
n = keyin_nr * i;
/* drive one KEYOUT pin low, read KEYIN pins */
sh_keysc_write(priv, KYOUTDR, 0xffff ^ (3 << (i * 2)));
udelay(pdata->delay);
tmp = sh_keysc_read(priv, KYINDR);
/* set bit if key press has been detected */
for (k = 0; k < keyin_nr; k++) {
if (tmp & (1 << k))
__set_bit(n + k, keys);
}
/* keep track of which KEYIN bits that have been set */
keyin_set |= tmp ^ ((1 << keyin_nr) - 1);
}
sh_keysc_level_mode(priv, keyin_set);
bitmap_complement(keys, keys, SH_KEYSC_MAXKEYS);
bitmap_and(keys1, keys1, keys, SH_KEYSC_MAXKEYS);
bitmap_or(keys0, keys0, keys, SH_KEYSC_MAXKEYS);
sh_keysc_map_dbg(&pdev->dev, keys, "keys");
} while (sh_keysc_read(priv, KYCR2) & 0x01);
sh_keysc_map_dbg(&pdev->dev, priv->last_keys, "last_keys");
sh_keysc_map_dbg(&pdev->dev, keys0, "keys0");
sh_keysc_map_dbg(&pdev->dev, keys1, "keys1");
for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
k = pdata->keycodes[i];
if (!k)
continue;
if (test_bit(i, keys0) == test_bit(i, priv->last_keys))
continue;
if (test_bit(i, keys1) || test_bit(i, keys0)) {
input_event(priv->input, EV_KEY, k, 1);
__set_bit(i, priv->last_keys);
}
if (!test_bit(i, keys1)) {
input_event(priv->input, EV_KEY, k, 0);
__clear_bit(i, priv->last_keys);
}
}
input_sync(priv->input);
return IRQ_HANDLED;
}
static int sh_keysc_probe(struct platform_device *pdev)
{
struct sh_keysc_priv *priv;
struct sh_keysc_info *pdata;
struct resource *res;
struct input_dev *input;
int i;
int irq, error;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data defined\n");
error = -EINVAL;
goto err0;
}
error = -ENXIO;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
goto err0;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
goto err0;
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
error = -ENOMEM;
goto err0;
}
platform_set_drvdata(pdev, priv);
memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
pdata = &priv->pdata;
priv->iomem_base = ioremap_nocache(res->start, resource_size(res));
if (priv->iomem_base == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -ENXIO;
goto err1;
}
priv->input = input_allocate_device();
if (!priv->input) {
dev_err(&pdev->dev, "failed to allocate input device\n");
error = -ENOMEM;
goto err2;
}
input = priv->input;
input->evbit[0] = BIT_MASK(EV_KEY);
input->name = pdev->name;
input->phys = "sh-keysc-keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->keycode = pdata->keycodes;
input->keycodesize = sizeof(pdata->keycodes[0]);
input->keycodemax = ARRAY_SIZE(pdata->keycodes);
error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT,
dev_name(&pdev->dev), pdev);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto err3;
}
for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
__set_bit(pdata->keycodes[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
goto err4;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
pdata->scan_timing);
sh_keysc_level_mode(priv, 0);
device_init_wakeup(&pdev->dev, 1);
return 0;
err4:
free_irq(irq, pdev);
err3:
input_free_device(input);
err2:
iounmap(priv->iomem_base);
err1:
platform_set_drvdata(pdev, NULL);
kfree(priv);
err0:
return error;
}
static int sh_keysc_remove(struct platform_device *pdev)
{
struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
input_unregister_device(priv->input);
free_irq(platform_get_irq(pdev, 0), pdev);
iounmap(priv->iomem_base);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(priv);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sh_keysc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
unsigned short value;
value = sh_keysc_read(priv, KYCR1);
if (device_may_wakeup(dev)) {
sh_keysc_write(priv, KYCR1, value | 0x80);
enable_irq_wake(irq);
} else {
sh_keysc_write(priv, KYCR1, value & ~0x80);
pm_runtime_put_sync(dev);
}
return 0;
}
static int sh_keysc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int irq = platform_get_irq(pdev, 0);
if (device_may_wakeup(dev))
disable_irq_wake(irq);
else
pm_runtime_get_sync(dev);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops,
sh_keysc_suspend, sh_keysc_resume);
static struct platform_driver sh_keysc_device_driver = {
.probe = sh_keysc_probe,
.remove = sh_keysc_remove,
.driver = {
.name = "sh_keysc",
.pm = &sh_keysc_dev_pm_ops,
}
};
module_platform_driver(sh_keysc_device_driver);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,404 @@
/*
* SPEAr Keyboard Driver
* Based on omap-keypad driver
*
* Copyright (C) 2010 ST Microelectronics
* Rajeev Kumar<rajeev-dlh.kumar@st.com>
*
* 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.
*/
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeup.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/platform_data/keyboard-spear.h>
/* Keyboard Registers */
#define MODE_CTL_REG 0x00
#define STATUS_REG 0x0C
#define DATA_REG 0x10
#define INTR_MASK 0x54
/* Register Values */
#define NUM_ROWS 16
#define NUM_COLS 16
#define MODE_CTL_PCLK_FREQ_SHIFT 9
#define MODE_CTL_PCLK_FREQ_MSK 0x7F
#define MODE_CTL_KEYBOARD (0x2 << 0)
#define MODE_CTL_SCAN_RATE_10 (0x0 << 2)
#define MODE_CTL_SCAN_RATE_20 (0x1 << 2)
#define MODE_CTL_SCAN_RATE_40 (0x2 << 2)
#define MODE_CTL_SCAN_RATE_80 (0x3 << 2)
#define MODE_CTL_KEYNUM_SHIFT 6
#define MODE_CTL_START_SCAN (0x1 << 8)
#define STATUS_DATA_AVAIL (0x1 << 1)
#define DATA_ROW_MASK 0xF0
#define DATA_COLUMN_MASK 0x0F
#define ROW_SHIFT 4
struct spear_kbd {
struct input_dev *input;
void __iomem *io_base;
struct clk *clk;
unsigned int irq;
unsigned int mode;
unsigned int suspended_rate;
unsigned short last_key;
unsigned short keycodes[NUM_ROWS * NUM_COLS];
bool rep;
bool irq_wake_enabled;
u32 mode_ctl_reg;
};
static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
{
struct spear_kbd *kbd = dev_id;
struct input_dev *input = kbd->input;
unsigned int key;
u32 sts, val;
sts = readl_relaxed(kbd->io_base + STATUS_REG);
if (!(sts & STATUS_DATA_AVAIL))
return IRQ_NONE;
if (kbd->last_key != KEY_RESERVED) {
input_report_key(input, kbd->last_key, 0);
kbd->last_key = KEY_RESERVED;
}
/* following reads active (row, col) pair */
val = readl_relaxed(kbd->io_base + DATA_REG) &
(DATA_ROW_MASK | DATA_COLUMN_MASK);
key = kbd->keycodes[val];
input_event(input, EV_MSC, MSC_SCAN, val);
input_report_key(input, key, 1);
input_sync(input);
kbd->last_key = key;
/* clear interrupt */
writel_relaxed(0, kbd->io_base + STATUS_REG);
return IRQ_HANDLED;
}
static int spear_kbd_open(struct input_dev *dev)
{
struct spear_kbd *kbd = input_get_drvdata(dev);
int error;
u32 val;
kbd->last_key = KEY_RESERVED;
error = clk_enable(kbd->clk);
if (error)
return error;
/* keyboard rate to be programmed is input clock (in MHz) - 1 */
val = clk_get_rate(kbd->clk) / 1000000 - 1;
val = (val & MODE_CTL_PCLK_FREQ_MSK) << MODE_CTL_PCLK_FREQ_SHIFT;
/* program keyboard */
val = MODE_CTL_SCAN_RATE_80 | MODE_CTL_KEYBOARD | val |
(kbd->mode << MODE_CTL_KEYNUM_SHIFT);
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
writel_relaxed(1, kbd->io_base + STATUS_REG);
/* start key scan */
val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
val |= MODE_CTL_START_SCAN;
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
return 0;
}
static void spear_kbd_close(struct input_dev *dev)
{
struct spear_kbd *kbd = input_get_drvdata(dev);
u32 val;
/* stop key scan */
val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
val &= ~MODE_CTL_START_SCAN;
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
clk_disable(kbd->clk);
kbd->last_key = KEY_RESERVED;
}
#ifdef CONFIG_OF
static int spear_kbd_parse_dt(struct platform_device *pdev,
struct spear_kbd *kbd)
{
struct device_node *np = pdev->dev.of_node;
int error;
u32 val, suspended_rate;
if (!np) {
dev_err(&pdev->dev, "Missing DT data\n");
return -EINVAL;
}
if (of_property_read_bool(np, "autorepeat"))
kbd->rep = true;
if (of_property_read_u32(np, "suspended_rate", &suspended_rate))
kbd->suspended_rate = suspended_rate;
error = of_property_read_u32(np, "st,mode", &val);
if (error) {
dev_err(&pdev->dev, "DT: Invalid or missing mode\n");
return error;
}
kbd->mode = val;
return 0;
}
#else
static inline int spear_kbd_parse_dt(struct platform_device *pdev,
struct spear_kbd *kbd)
{
return -ENOSYS;
}
#endif
static int spear_kbd_probe(struct platform_device *pdev)
{
struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev);
const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL;
struct spear_kbd *kbd;
struct input_dev *input_dev;
struct resource *res;
int irq;
int error;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no keyboard resource defined\n");
return -EBUSY;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "not able to get irq for the device\n");
return irq;
}
kbd = devm_kzalloc(&pdev->dev, sizeof(*kbd), GFP_KERNEL);
if (!kbd) {
dev_err(&pdev->dev, "not enough memory for driver data\n");
return -ENOMEM;
}
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev) {
dev_err(&pdev->dev, "unable to allocate input device\n");
return -ENOMEM;
}
kbd->input = input_dev;
kbd->irq = irq;
if (!pdata) {
error = spear_kbd_parse_dt(pdev, kbd);
if (error)
return error;
} else {
kbd->mode = pdata->mode;
kbd->rep = pdata->rep;
kbd->suspended_rate = pdata->suspended_rate;
}
kbd->io_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(kbd->io_base))
return PTR_ERR(kbd->io_base);
kbd->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbd->clk))
return PTR_ERR(kbd->clk);
input_dev->name = "Spear Keyboard";
input_dev->phys = "keyboard/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->open = spear_kbd_open;
input_dev->close = spear_kbd_close;
error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS,
kbd->keycodes, input_dev);
if (error) {
dev_err(&pdev->dev, "Failed to build keymap\n");
return error;
}
if (kbd->rep)
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, kbd);
error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0,
"keyboard", kbd);
if (error) {
dev_err(&pdev->dev, "request_irq failed\n");
return error;
}
error = clk_prepare(kbd->clk);
if (error)
return error;
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev, "Unable to register keyboard device\n");
clk_unprepare(kbd->clk);
return error;
}
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, kbd);
return 0;
}
static int spear_kbd_remove(struct platform_device *pdev)
{
struct spear_kbd *kbd = platform_get_drvdata(pdev);
input_unregister_device(kbd->input);
clk_unprepare(kbd->clk);
device_init_wakeup(&pdev->dev, 0);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
static int spear_kbd_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spear_kbd *kbd = platform_get_drvdata(pdev);
struct input_dev *input_dev = kbd->input;
unsigned int rate = 0, mode_ctl_reg, val;
mutex_lock(&input_dev->mutex);
/* explicitly enable clock as we may program device */
clk_enable(kbd->clk);
mode_ctl_reg = readl_relaxed(kbd->io_base + MODE_CTL_REG);
if (device_may_wakeup(&pdev->dev)) {
if (!enable_irq_wake(kbd->irq))
kbd->irq_wake_enabled = true;
/*
* reprogram the keyboard operating frequency as on some
* platform it may change during system suspended
*/
if (kbd->suspended_rate)
rate = kbd->suspended_rate / 1000000 - 1;
else
rate = clk_get_rate(kbd->clk) / 1000000 - 1;
val = mode_ctl_reg &
~(MODE_CTL_PCLK_FREQ_MSK << MODE_CTL_PCLK_FREQ_SHIFT);
val |= (rate & MODE_CTL_PCLK_FREQ_MSK)
<< MODE_CTL_PCLK_FREQ_SHIFT;
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
} else {
if (input_dev->users) {
writel_relaxed(mode_ctl_reg & ~MODE_CTL_START_SCAN,
kbd->io_base + MODE_CTL_REG);
clk_disable(kbd->clk);
}
}
/* store current configuration */
if (input_dev->users)
kbd->mode_ctl_reg = mode_ctl_reg;
/* restore previous clk state */
clk_disable(kbd->clk);
mutex_unlock(&input_dev->mutex);
return 0;
}
static int spear_kbd_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spear_kbd *kbd = platform_get_drvdata(pdev);
struct input_dev *input_dev = kbd->input;
mutex_lock(&input_dev->mutex);
if (device_may_wakeup(&pdev->dev)) {
if (kbd->irq_wake_enabled) {
kbd->irq_wake_enabled = false;
disable_irq_wake(kbd->irq);
}
} else {
if (input_dev->users)
clk_enable(kbd->clk);
}
/* restore current configuration */
if (input_dev->users)
writel_relaxed(kbd->mode_ctl_reg, kbd->io_base + MODE_CTL_REG);
mutex_unlock(&input_dev->mutex);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
#ifdef CONFIG_OF
static const struct of_device_id spear_kbd_id_table[] = {
{ .compatible = "st,spear300-kbd" },
{}
};
MODULE_DEVICE_TABLE(of, spear_kbd_id_table);
#endif
static struct platform_driver spear_kbd_driver = {
.probe = spear_kbd_probe,
.remove = spear_kbd_remove,
.driver = {
.name = "keyboard",
.owner = THIS_MODULE,
.pm = &spear_kbd_pm_ops,
.of_match_table = of_match_ptr(spear_kbd_id_table),
},
};
module_platform_driver(spear_kbd_driver);
MODULE_AUTHOR("Rajeev Kumar");
MODULE_DESCRIPTION("SPEAr Keyboard Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,399 @@
/*
* Copyright (C) ST-Ericsson SA 2010
*
* License Terms: GNU General Public License, version 2
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/input/matrix_keypad.h>
#include <linux/mfd/stmpe.h>
/* These are at the same addresses in all STMPE variants */
#define STMPE_KPC_COL 0x60
#define STMPE_KPC_ROW_MSB 0x61
#define STMPE_KPC_ROW_LSB 0x62
#define STMPE_KPC_CTRL_MSB 0x63
#define STMPE_KPC_CTRL_LSB 0x64
#define STMPE_KPC_COMBI_KEY_0 0x65
#define STMPE_KPC_COMBI_KEY_1 0x66
#define STMPE_KPC_COMBI_KEY_2 0x67
#define STMPE_KPC_DATA_BYTE0 0x68
#define STMPE_KPC_DATA_BYTE1 0x69
#define STMPE_KPC_DATA_BYTE2 0x6a
#define STMPE_KPC_DATA_BYTE3 0x6b
#define STMPE_KPC_DATA_BYTE4 0x6c
#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0)
#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1)
#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4)
#define STMPE_KPC_ROW_MSB_ROWS 0xff
#define STMPE_KPC_DATA_UP (0x1 << 7)
#define STMPE_KPC_DATA_ROW (0xf << 3)
#define STMPE_KPC_DATA_COL (0x7 << 0)
#define STMPE_KPC_DATA_NOKEY_MASK 0x78
#define STMPE_KEYPAD_MAX_DEBOUNCE 127
#define STMPE_KEYPAD_MAX_SCAN_COUNT 15
#define STMPE_KEYPAD_MAX_ROWS 8
#define STMPE_KEYPAD_MAX_COLS 8
#define STMPE_KEYPAD_ROW_SHIFT 3
#define STMPE_KEYPAD_KEYMAP_SIZE \
(STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
/**
* struct stmpe_keypad_variant - model-specific attributes
* @auto_increment: whether the KPC_DATA_BYTE register address
* auto-increments on multiple read
* @num_data: number of data bytes
* @num_normal_data: number of normal keys' data bytes
* @max_cols: maximum number of columns supported
* @max_rows: maximum number of rows supported
* @col_gpios: bitmask of gpios which can be used for columns
* @row_gpios: bitmask of gpios which can be used for rows
*/
struct stmpe_keypad_variant {
bool auto_increment;
int num_data;
int num_normal_data;
int max_cols;
int max_rows;
unsigned int col_gpios;
unsigned int row_gpios;
};
static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE1601] = {
.auto_increment = true,
.num_data = 5,
.num_normal_data = 3,
.max_cols = 8,
.max_rows = 8,
.col_gpios = 0x000ff, /* GPIO 0 - 7 */
.row_gpios = 0x0ff00, /* GPIO 8 - 15 */
},
[STMPE2401] = {
.auto_increment = false,
.num_data = 3,
.num_normal_data = 2,
.max_cols = 8,
.max_rows = 12,
.col_gpios = 0x0000ff, /* GPIO 0 - 7*/
.row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
},
[STMPE2403] = {
.auto_increment = true,
.num_data = 5,
.num_normal_data = 3,
.max_cols = 8,
.max_rows = 12,
.col_gpios = 0x0000ff, /* GPIO 0 - 7*/
.row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
},
};
struct stmpe_keypad {
struct stmpe *stmpe;
struct input_dev *input;
const struct stmpe_keypad_variant *variant;
const struct stmpe_keypad_platform_data *plat;
unsigned int rows;
unsigned int cols;
unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE];
};
static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
{
const struct stmpe_keypad_variant *variant = keypad->variant;
struct stmpe *stmpe = keypad->stmpe;
int ret;
int i;
if (variant->auto_increment)
return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
variant->num_data, data);
for (i = 0; i < variant->num_data; i++) {
ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
if (ret < 0)
return ret;
data[i] = ret;
}
return 0;
}
static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
{
struct stmpe_keypad *keypad = dev;
struct input_dev *input = keypad->input;
const struct stmpe_keypad_variant *variant = keypad->variant;
u8 fifo[variant->num_data];
int ret;
int i;
ret = stmpe_keypad_read_data(keypad, fifo);
if (ret < 0)
return IRQ_NONE;
for (i = 0; i < variant->num_normal_data; i++) {
u8 data = fifo[i];
int row = (data & STMPE_KPC_DATA_ROW) >> 3;
int col = data & STMPE_KPC_DATA_COL;
int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
bool up = data & STMPE_KPC_DATA_UP;
if ((data & STMPE_KPC_DATA_NOKEY_MASK)
== STMPE_KPC_DATA_NOKEY_MASK)
continue;
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keypad->keymap[code], !up);
input_sync(input);
}
return IRQ_HANDLED;
}
static int stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
{
const struct stmpe_keypad_variant *variant = keypad->variant;
unsigned int col_gpios = variant->col_gpios;
unsigned int row_gpios = variant->row_gpios;
struct stmpe *stmpe = keypad->stmpe;
unsigned int pins = 0;
int i;
/*
* Figure out which pins need to be set to the keypad alternate
* function.
*
* {cols,rows}_gpios are bitmasks of which pins on the chip can be used
* for the keypad.
*
* keypad->{cols,rows} are a bitmask of which pins (of the ones useable
* for the keypad) are used on the board.
*/
for (i = 0; i < variant->max_cols; i++) {
int num = __ffs(col_gpios);
if (keypad->cols & (1 << i))
pins |= 1 << num;
col_gpios &= ~(1 << num);
}
for (i = 0; i < variant->max_rows; i++) {
int num = __ffs(row_gpios);
if (keypad->rows & (1 << i))
pins |= 1 << num;
row_gpios &= ~(1 << num);
}
return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
}
static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
{
const struct stmpe_keypad_platform_data *plat = keypad->plat;
const struct stmpe_keypad_variant *variant = keypad->variant;
struct stmpe *stmpe = keypad->stmpe;
int ret;
if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
return -EINVAL;
if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
return -EINVAL;
ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
if (ret < 0)
return ret;
ret = stmpe_keypad_altfunc_init(keypad);
if (ret < 0)
return ret;
ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
if (ret < 0)
return ret;
ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
if (ret < 0)
return ret;
if (variant->max_rows > 8) {
ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
STMPE_KPC_ROW_MSB_ROWS,
keypad->rows >> 8);
if (ret < 0)
return ret;
}
ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
STMPE_KPC_CTRL_MSB_SCAN_COUNT,
plat->scan_count << 4);
if (ret < 0)
return ret;
return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
STMPE_KPC_CTRL_LSB_SCAN |
STMPE_KPC_CTRL_LSB_DEBOUNCE,
STMPE_KPC_CTRL_LSB_SCAN |
(plat->debounce_ms << 1));
}
static void stmpe_keypad_fill_used_pins(struct stmpe_keypad *keypad)
{
int row, col;
for (row = 0; row < STMPE_KEYPAD_MAX_ROWS; row++) {
for (col = 0; col < STMPE_KEYPAD_MAX_COLS; col++) {
int code = MATRIX_SCAN_CODE(row, col,
STMPE_KEYPAD_ROW_SHIFT);
if (keypad->keymap[code] != KEY_RESERVED) {
keypad->rows |= 1 << row;
keypad->cols |= 1 << col;
}
}
}
}
#ifdef CONFIG_OF
static const struct stmpe_keypad_platform_data *
stmpe_keypad_of_probe(struct device *dev)
{
struct device_node *np = dev->of_node;
struct stmpe_keypad_platform_data *plat;
if (!np)
return ERR_PTR(-ENODEV);
plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL);
if (!plat)
return ERR_PTR(-ENOMEM);
of_property_read_u32(np, "debounce-interval", &plat->debounce_ms);
of_property_read_u32(np, "st,scan-count", &plat->scan_count);
plat->no_autorepeat = of_property_read_bool(np, "st,no-autorepeat");
return plat;
}
#else
static inline const struct stmpe_keypad_platform_data *
stmpe_keypad_of_probe(struct device *dev)
{
return ERR_PTR(-EINVAL);
}
#endif
static int stmpe_keypad_probe(struct platform_device *pdev)
{
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
const struct stmpe_keypad_platform_data *plat;
struct stmpe_keypad *keypad;
struct input_dev *input;
int error;
int irq;
plat = stmpe->pdata->keypad;
if (!plat) {
plat = stmpe_keypad_of_probe(&pdev->dev);
if (IS_ERR(plat))
return PTR_ERR(plat);
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
keypad = devm_kzalloc(&pdev->dev, sizeof(struct stmpe_keypad),
GFP_KERNEL);
if (!keypad)
return -ENOMEM;
input = devm_input_allocate_device(&pdev->dev);
if (!input)
return -ENOMEM;
input->name = "STMPE keypad";
input->id.bustype = BUS_I2C;
input->dev.parent = &pdev->dev;
error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
STMPE_KEYPAD_MAX_ROWS,
STMPE_KEYPAD_MAX_COLS,
keypad->keymap, input);
if (error)
return error;
input_set_capability(input, EV_MSC, MSC_SCAN);
if (!plat->no_autorepeat)
__set_bit(EV_REP, input->evbit);
stmpe_keypad_fill_used_pins(keypad);
keypad->stmpe = stmpe;
keypad->plat = plat;
keypad->input = input;
keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
error = stmpe_keypad_chip_init(keypad);
if (error < 0)
return error;
error = devm_request_threaded_irq(&pdev->dev, irq,
NULL, stmpe_keypad_irq,
IRQF_ONESHOT, "stmpe-keypad", keypad);
if (error) {
dev_err(&pdev->dev, "unable to get irq: %d\n", error);
return error;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev,
"unable to register input device: %d\n", error);
return error;
}
platform_set_drvdata(pdev, keypad);
return 0;
}
static int stmpe_keypad_remove(struct platform_device *pdev)
{
struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
stmpe_disable(keypad->stmpe, STMPE_BLOCK_KEYPAD);
return 0;
}
static struct platform_driver stmpe_keypad_driver = {
.driver.name = "stmpe-keypad",
.driver.owner = THIS_MODULE,
.probe = stmpe_keypad_probe,
.remove = stmpe_keypad_remove,
};
module_platform_driver(stmpe_keypad_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("STMPExxxx keypad driver");
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");

View File

@@ -0,0 +1,173 @@
/*
* Stowaway keyboard driver for Linux
*/
/*
* Copyright (c) 2006 Marek Vasut
*
* Based on Newton keyboard driver for Linux
* by Justin Cormack
*/
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail:
* Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#define DRIVER_DESC "Stowaway keyboard driver"
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define SKBD_KEY_MASK 0x7f
#define SKBD_RELEASE 0x80
static unsigned char skbd_keycode[128] = {
KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7,
0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE,
KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE,
KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0,
0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0,
0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N,
KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC,
KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P,
KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT,
KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0,
KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0
};
struct skbd {
unsigned char keycode[128];
struct input_dev *dev;
struct serio *serio;
char phys[32];
};
static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data,
unsigned int flags)
{
struct skbd *skbd = serio_get_drvdata(serio);
struct input_dev *dev = skbd->dev;
if (skbd->keycode[data & SKBD_KEY_MASK]) {
input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK],
!(data & SKBD_RELEASE));
input_sync(dev);
}
return IRQ_HANDLED;
}
static int skbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct skbd *skbd;
struct input_dev *input_dev;
int err = -ENOMEM;
int i;
skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!skbd || !input_dev)
goto fail1;
skbd->serio = serio;
skbd->dev = input_dev;
snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys);
memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode));
input_dev->name = "Stowaway Keyboard";
input_dev->phys = skbd->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_STOWAWAY;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_dev->keycode = skbd->keycode;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(skbd_keycode);
for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++)
set_bit(skbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
serio_set_drvdata(serio, skbd);
err = serio_open(serio, drv);
if (err)
goto fail2;
err = input_register_device(skbd->dev);
if (err)
goto fail3;
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(skbd);
return err;
}
static void skbd_disconnect(struct serio *serio)
{
struct skbd *skbd = serio_get_drvdata(serio);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_unregister_device(skbd->dev);
kfree(skbd);
}
static struct serio_device_id skbd_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_STOWAWAY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, skbd_serio_ids);
static struct serio_driver skbd_drv = {
.driver = {
.name = "stowaway",
},
.description = DRIVER_DESC,
.id_table = skbd_serio_ids,
.interrupt = skbd_interrupt,
.connect = skbd_connect,
.disconnect = skbd_disconnect,
};
module_serio_driver(skbd_drv);

View File

@@ -0,0 +1,372 @@
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Sun keyboard driver for Linux
*/
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#define DRIVER_DESC "Sun keyboard driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static unsigned char sunkbd_keycode[128] = {
0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
};
#define SUNKBD_CMD_RESET 0x1
#define SUNKBD_CMD_BELLON 0x2
#define SUNKBD_CMD_BELLOFF 0x3
#define SUNKBD_CMD_CLICK 0xa
#define SUNKBD_CMD_NOCLICK 0xb
#define SUNKBD_CMD_SETLED 0xe
#define SUNKBD_CMD_LAYOUT 0xf
#define SUNKBD_RET_RESET 0xff
#define SUNKBD_RET_ALLUP 0x7f
#define SUNKBD_RET_LAYOUT 0xfe
#define SUNKBD_LAYOUT_5_MASK 0x20
#define SUNKBD_RELEASE 0x80
#define SUNKBD_KEY 0x7f
/*
* Per-keyboard data.
*/
struct sunkbd {
unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
struct input_dev *dev;
struct serio *serio;
struct work_struct tq;
wait_queue_head_t wait;
char name[64];
char phys[32];
char type;
bool enabled;
volatile s8 reset;
volatile s8 layout;
};
/*
* sunkbd_interrupt() is called by the low level driver when a character
* is received.
*/
static irqreturn_t sunkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct sunkbd *sunkbd = serio_get_drvdata(serio);
if (sunkbd->reset <= -1) {
/*
* If cp[i] is 0xff, sunkbd->reset will stay -1.
* The keyboard sends 0xff 0xff 0xID on powerup.
*/
sunkbd->reset = data;
wake_up_interruptible(&sunkbd->wait);
goto out;
}
if (sunkbd->layout == -1) {
sunkbd->layout = data;
wake_up_interruptible(&sunkbd->wait);
goto out;
}
switch (data) {
case SUNKBD_RET_RESET:
schedule_work(&sunkbd->tq);
sunkbd->reset = -1;
break;
case SUNKBD_RET_LAYOUT:
sunkbd->layout = -1;
break;
case SUNKBD_RET_ALLUP: /* All keys released */
break;
default:
if (!sunkbd->enabled)
break;
if (sunkbd->keycode[data & SUNKBD_KEY]) {
input_report_key(sunkbd->dev,
sunkbd->keycode[data & SUNKBD_KEY],
!(data & SUNKBD_RELEASE));
input_sync(sunkbd->dev);
} else {
printk(KERN_WARNING
"sunkbd.c: Unknown key (scancode %#x) %s.\n",
data & SUNKBD_KEY,
data & SUNKBD_RELEASE ? "released" : "pressed");
}
}
out:
return IRQ_HANDLED;
}
/*
* sunkbd_event() handles events from the input module.
*/
static int sunkbd_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct sunkbd *sunkbd = input_get_drvdata(dev);
switch (type) {
case EV_LED:
serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
serio_write(sunkbd->serio,
(!!test_bit(LED_CAPSL, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) |
(!!test_bit(LED_COMPOSE, dev->led) << 1) |
!!test_bit(LED_NUML, dev->led));
return 0;
case EV_SND:
switch (code) {
case SND_CLICK:
serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
return 0;
case SND_BELL:
serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
return 0;
}
break;
}
return -1;
}
/*
* sunkbd_initialize() checks for a Sun keyboard attached, and determines
* its type.
*/
static int sunkbd_initialize(struct sunkbd *sunkbd)
{
sunkbd->reset = -2;
serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
if (sunkbd->reset < 0)
return -1;
sunkbd->type = sunkbd->reset;
if (sunkbd->type == 4) { /* Type 4 keyboard */
sunkbd->layout = -2;
serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
wait_event_interruptible_timeout(sunkbd->wait,
sunkbd->layout >= 0, HZ / 4);
if (sunkbd->layout < 0)
return -1;
if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
sunkbd->type = 5;
}
return 0;
}
/*
* sunkbd_reinit() sets leds and beeps to a state the computer remembers they
* were in.
*/
static void sunkbd_reinit(struct work_struct *work)
{
struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
serio_write(sunkbd->serio,
(!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) |
(!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
(!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
!!test_bit(LED_NUML, sunkbd->dev->led));
serio_write(sunkbd->serio,
SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
serio_write(sunkbd->serio,
SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
}
static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
{
serio_pause_rx(sunkbd->serio);
sunkbd->enabled = enable;
serio_continue_rx(sunkbd->serio);
}
/*
* sunkbd_connect() probes for a Sun keyboard and fills the necessary
* structures.
*/
static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct sunkbd *sunkbd;
struct input_dev *input_dev;
int err = -ENOMEM;
int i;
sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!sunkbd || !input_dev)
goto fail1;
sunkbd->serio = serio;
sunkbd->dev = input_dev;
init_waitqueue_head(&sunkbd->wait);
INIT_WORK(&sunkbd->tq, sunkbd_reinit);
snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
serio_set_drvdata(serio, sunkbd);
err = serio_open(serio, drv);
if (err)
goto fail2;
if (sunkbd_initialize(sunkbd) < 0) {
err = -ENODEV;
goto fail3;
}
snprintf(sunkbd->name, sizeof(sunkbd->name),
"Sun Type %d keyboard", sunkbd->type);
memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
input_dev->name = sunkbd->name;
input_dev->phys = sunkbd->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_SUNKBD;
input_dev->id.product = sunkbd->type;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_set_drvdata(input_dev, sunkbd);
input_dev->event = sunkbd_event;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
input_dev->keycode = sunkbd->keycode;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
__set_bit(sunkbd->keycode[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
sunkbd_enable(sunkbd, true);
err = input_register_device(sunkbd->dev);
if (err)
goto fail4;
return 0;
fail4: sunkbd_enable(sunkbd, false);
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(sunkbd);
return err;
}
/*
* sunkbd_disconnect() unregisters and closes behind us.
*/
static void sunkbd_disconnect(struct serio *serio)
{
struct sunkbd *sunkbd = serio_get_drvdata(serio);
sunkbd_enable(sunkbd, false);
input_unregister_device(sunkbd->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
kfree(sunkbd);
}
static struct serio_device_id sunkbd_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_SUNKBD,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_UNKNOWN, /* sunkbd does probe */
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
static struct serio_driver sunkbd_drv = {
.driver = {
.name = "sunkbd",
},
.description = DRIVER_DESC,
.id_table = sunkbd_serio_ids,
.interrupt = sunkbd_interrupt,
.connect = sunkbd_connect,
.disconnect = sunkbd_disconnect,
};
module_serio_driver(sunkbd_drv);

View File

@@ -0,0 +1,458 @@
/*
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com>
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
*
* License Terms: GNU General Public License, version 2
*
* TC35893 MFD Keypad Controller driver
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/matrix_keypad.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/mfd/tc3589x.h>
/* Maximum supported keypad matrix row/columns size */
#define TC3589x_MAX_KPROW 8
#define TC3589x_MAX_KPCOL 12
/* keypad related Constants */
#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF
#define DEDICATED_KEY_VAL 0xFF
/* Pull up/down masks */
#define TC3589x_NO_PULL_MASK 0x0
#define TC3589x_PULL_DOWN_MASK 0x1
#define TC3589x_PULL_UP_MASK 0x2
#define TC3589x_PULLUP_ALL_MASK 0xAA
#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))
/* Bit masks for IOCFG register */
#define IOCFG_BALLCFG 0x01
#define IOCFG_IG 0x08
#define KP_EVCODE_COL_MASK 0x0F
#define KP_EVCODE_ROW_MASK 0x70
#define KP_RELEASE_EVT_MASK 0x80
#define KP_ROW_SHIFT 4
#define KP_NO_VALID_KEY_MASK 0x7F
/* bit masks for RESTCTRL register */
#define TC3589x_KBDRST 0x2
#define TC3589x_IRQRST 0x10
#define TC3589x_RESET_ALL 0x1B
/* KBDMFS register bit mask */
#define TC3589x_KBDMFS_EN 0x1
/* CLKEN register bitmask */
#define KPD_CLK_EN 0x1
/* RSTINTCLR register bit mask */
#define IRQ_CLEAR 0x1
/* bit masks for keyboard interrupts*/
#define TC3589x_EVT_LOSS_INT 0x8
#define TC3589x_EVT_INT 0x4
#define TC3589x_KBD_LOSS_INT 0x2
#define TC3589x_KBD_INT 0x1
/* bit masks for keyboard interrupt clear*/
#define TC3589x_EVT_INT_CLR 0x2
#define TC3589x_KBD_INT_CLR 0x1
/**
* struct tc_keypad - data structure used by keypad driver
* @tc3589x: pointer to tc35893
* @input: pointer to input device object
* @board: keypad platform device
* @krow: number of rows
* @kcol: number of columns
* @keymap: matrix scan code table for keycodes
* @keypad_stopped: holds keypad status
*/
struct tc_keypad {
struct tc3589x *tc3589x;
struct input_dev *input;
const struct tc3589x_keypad_platform_data *board;
unsigned int krow;
unsigned int kcol;
unsigned short *keymap;
bool keypad_stopped;
};
static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
{
int ret;
struct tc3589x *tc3589x = keypad->tc3589x;
const struct tc3589x_keypad_platform_data *board = keypad->board;
/* validate platform configuration */
if (board->kcol > TC3589x_MAX_KPCOL || board->krow > TC3589x_MAX_KPROW)
return -EINVAL;
/* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,
(board->krow << KP_ROW_SHIFT) | board->kcol);
if (ret < 0)
return ret;
/* configure dedicated key config, no dedicated key selected */
ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL);
if (ret < 0)
return ret;
ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL);
if (ret < 0)
return ret;
/* Configure settle time */
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG,
board->settle_time);
if (ret < 0)
return ret;
/* Configure debounce time */
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE,
board->debounce_period);
if (ret < 0)
return ret;
/* Start of initialise keypad GPIOs */
ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);
if (ret < 0)
return ret;
/* Configure pull-up resistors for all row GPIOs */
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB,
TC3589x_PULLUP_ALL_MASK);
if (ret < 0)
return ret;
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB,
TC3589x_PULLUP_ALL_MASK);
if (ret < 0)
return ret;
/* Configure pull-up resistors for all column GPIOs */
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB,
TC3589x_PULLUP_ALL_MASK);
if (ret < 0)
return ret;
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB,
TC3589x_PULLUP_ALL_MASK);
if (ret < 0)
return ret;
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB,
TC3589x_PULLUP_ALL_MASK);
return ret;
}
#define TC35893_DATA_REGS 4
#define TC35893_KEYCODE_FIFO_EMPTY 0x7f
#define TC35893_KEYCODE_FIFO_CLEAR 0xff
#define TC35893_KEYPAD_ROW_SHIFT 0x3
static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)
{
struct tc_keypad *keypad = dev;
struct tc3589x *tc3589x = keypad->tc3589x;
u8 i, row_index, col_index, kbd_code, up;
u8 code;
for (i = 0; i < TC35893_DATA_REGS * 2; i++) {
kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);
/* loop till fifo is empty and no more keys are pressed */
if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY ||
kbd_code == TC35893_KEYCODE_FIFO_CLEAR)
continue;
/* valid key is found */
col_index = kbd_code & KP_EVCODE_COL_MASK;
row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
code = MATRIX_SCAN_CODE(row_index, col_index,
TC35893_KEYPAD_ROW_SHIFT);
up = kbd_code & KP_RELEASE_EVT_MASK;
input_event(keypad->input, EV_MSC, MSC_SCAN, code);
input_report_key(keypad->input, keypad->keymap[code], !up);
input_sync(keypad->input);
}
/* clear IRQ */
tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
/* enable IRQ */
tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
return IRQ_HANDLED;
}
static int tc3589x_keypad_enable(struct tc_keypad *keypad)
{
struct tc3589x *tc3589x = keypad->tc3589x;
int ret;
/* pull the keypad module out of reset */
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0);
if (ret < 0)
return ret;
/* configure KBDMFS */
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN);
if (ret < 0)
return ret;
/* enable the keypad clock */
ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);
if (ret < 0)
return ret;
/* clear pending IRQs */
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);
if (ret < 0)
return ret;
/* enable the IRQs */
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,
TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
if (ret < 0)
return ret;
keypad->keypad_stopped = false;
return ret;
}
static int tc3589x_keypad_disable(struct tc_keypad *keypad)
{
struct tc3589x *tc3589x = keypad->tc3589x;
int ret;
/* clear IRQ */
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
if (ret < 0)
return ret;
/* disable all interrupts */
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);
if (ret < 0)
return ret;
/* disable the keypad module */
ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);
if (ret < 0)
return ret;
/* put the keypad module into reset */
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1);
keypad->keypad_stopped = true;
return ret;
}
static int tc3589x_keypad_open(struct input_dev *input)
{
int error;
struct tc_keypad *keypad = input_get_drvdata(input);
/* enable the keypad module */
error = tc3589x_keypad_enable(keypad);
if (error < 0) {
dev_err(&input->dev, "failed to enable keypad module\n");
return error;
}
error = tc3589x_keypad_init_key_hardware(keypad);
if (error < 0) {
dev_err(&input->dev, "failed to configure keypad module\n");
return error;
}
return 0;
}
static void tc3589x_keypad_close(struct input_dev *input)
{
struct tc_keypad *keypad = input_get_drvdata(input);
/* disable the keypad module */
tc3589x_keypad_disable(keypad);
}
static int tc3589x_keypad_probe(struct platform_device *pdev)
{
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
struct tc_keypad *keypad;
struct input_dev *input;
const struct tc3589x_keypad_platform_data *plat;
int error, irq;
plat = tc3589x->pdata->keypad;
if (!plat) {
dev_err(&pdev->dev, "invalid keypad platform data\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
input = input_allocate_device();
if (!keypad || !input) {
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
error = -ENOMEM;
goto err_free_mem;
}
keypad->board = plat;
keypad->input = input;
keypad->tc3589x = tc3589x;
input->id.bustype = BUS_I2C;
input->name = pdev->name;
input->dev.parent = &pdev->dev;
input->open = tc3589x_keypad_open;
input->close = tc3589x_keypad_close;
error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL,
NULL, input);
if (error) {
dev_err(&pdev->dev, "Failed to build keymap\n");
goto err_free_mem;
}
keypad->keymap = input->keycode;
input_set_capability(input, EV_MSC, MSC_SCAN);
if (!plat->no_autorepeat)
__set_bit(EV_REP, input->evbit);
input_set_drvdata(input, keypad);
error = request_threaded_irq(irq, NULL,
tc3589x_keypad_irq, plat->irqtype,
"tc3589x-keypad", keypad);
if (error < 0) {
dev_err(&pdev->dev,
"Could not allocate irq %d,error %d\n",
irq, error);
goto err_free_mem;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "Could not register input device\n");
goto err_free_irq;
}
/* let platform decide if keypad is a wakeup source or not */
device_init_wakeup(&pdev->dev, plat->enable_wakeup);
device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);
platform_set_drvdata(pdev, keypad);
return 0;
err_free_irq:
free_irq(irq, keypad);
err_free_mem:
input_free_device(input);
kfree(keypad);
return error;
}
static int tc3589x_keypad_remove(struct platform_device *pdev)
{
struct tc_keypad *keypad = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
if (!keypad->keypad_stopped)
tc3589x_keypad_disable(keypad);
free_irq(irq, keypad);
input_unregister_device(keypad->input);
kfree(keypad);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tc3589x_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tc_keypad *keypad = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
/* keypad is already off; we do nothing */
if (keypad->keypad_stopped)
return 0;
/* if device is not a wakeup source, disable it for powersave */
if (!device_may_wakeup(&pdev->dev))
tc3589x_keypad_disable(keypad);
else
enable_irq_wake(irq);
return 0;
}
static int tc3589x_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tc_keypad *keypad = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
if (!keypad->keypad_stopped)
return 0;
/* enable the device to resume normal operations */
if (!device_may_wakeup(&pdev->dev))
tc3589x_keypad_enable(keypad);
else
disable_irq_wake(irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
tc3589x_keypad_suspend, tc3589x_keypad_resume);
static struct platform_driver tc3589x_keypad_driver = {
.driver = {
.name = "tc3589x-keypad",
.owner = THIS_MODULE,
.pm = &tc3589x_keypad_dev_pm_ops,
},
.probe = tc3589x_keypad_probe,
.remove = tc3589x_keypad_remove,
};
module_platform_driver(tc3589x_keypad_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
MODULE_DESCRIPTION("TC35893 Keypad Driver");
MODULE_ALIAS("platform:tc3589x-keypad");

View File

@@ -0,0 +1,383 @@
/*
* Driver for keys on TCA6416 I2C IO expander
*
* Copyright (C) 2010 Texas Instruments
*
* Author : Sriramakrishnan.A.G. <srk@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/tca6416_keypad.h>
#define TCA6416_INPUT 0
#define TCA6416_OUTPUT 1
#define TCA6416_INVERT 2
#define TCA6416_DIRECTION 3
static const struct i2c_device_id tca6416_id[] = {
{ "tca6416-keys", 16, },
{ "tca6408-keys", 8, },
{ }
};
MODULE_DEVICE_TABLE(i2c, tca6416_id);
struct tca6416_drv_data {
struct input_dev *input;
struct tca6416_button data[0];
};
struct tca6416_keypad_chip {
uint16_t reg_output;
uint16_t reg_direction;
uint16_t reg_input;
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
int io_size;
int irqnum;
u16 pinmask;
bool use_polling;
struct tca6416_button buttons[0];
};
static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
{
int error;
error = chip->io_size > 8 ?
i2c_smbus_write_word_data(chip->client, reg << 1, val) :
i2c_smbus_write_byte_data(chip->client, reg, val);
if (error < 0) {
dev_err(&chip->client->dev,
"%s failed, reg: %d, val: %d, error: %d\n",
__func__, reg, val, error);
return error;
}
return 0;
}
static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
{
int retval;
retval = chip->io_size > 8 ?
i2c_smbus_read_word_data(chip->client, reg << 1) :
i2c_smbus_read_byte_data(chip->client, reg);
if (retval < 0) {
dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
__func__, reg, retval);
return retval;
}
*val = (u16)retval;
return 0;
}
static void tca6416_keys_scan(struct tca6416_keypad_chip *chip)
{
struct input_dev *input = chip->input;
u16 reg_val, val;
int error, i, pin_index;
error = tca6416_read_reg(chip, TCA6416_INPUT, &reg_val);
if (error)
return;
reg_val &= chip->pinmask;
/* Figure out which lines have changed */
val = reg_val ^ chip->reg_input;
chip->reg_input = reg_val;
for (i = 0, pin_index = 0; i < 16; i++) {
if (val & (1 << i)) {
struct tca6416_button *button = &chip->buttons[pin_index];
unsigned int type = button->type ?: EV_KEY;
int state = ((reg_val & (1 << i)) ? 1 : 0)
^ button->active_low;
input_event(input, type, button->code, !!state);
input_sync(input);
}
if (chip->pinmask & (1 << i))
pin_index++;
}
}
/*
* This is threaded IRQ handler and this can (and will) sleep.
*/
static irqreturn_t tca6416_keys_isr(int irq, void *dev_id)
{
struct tca6416_keypad_chip *chip = dev_id;
tca6416_keys_scan(chip);
return IRQ_HANDLED;
}
static void tca6416_keys_work_func(struct work_struct *work)
{
struct tca6416_keypad_chip *chip =
container_of(work, struct tca6416_keypad_chip, dwork.work);
tca6416_keys_scan(chip);
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
}
static int tca6416_keys_open(struct input_dev *dev)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
/* Get initial device state in case it has switches */
tca6416_keys_scan(chip);
if (chip->use_polling)
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
else
enable_irq(chip->irqnum);
return 0;
}
static void tca6416_keys_close(struct input_dev *dev)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
if (chip->use_polling)
cancel_delayed_work_sync(&chip->dwork);
else
disable_irq(chip->irqnum);
}
static int tca6416_setup_registers(struct tca6416_keypad_chip *chip)
{
int error;
error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
if (error)
return error;
/* ensure that keypad pins are set to input */
error = tca6416_write_reg(chip, TCA6416_DIRECTION,
chip->reg_direction | chip->pinmask);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input);
if (error)
return error;
chip->reg_input &= chip->pinmask;
return 0;
}
static int tca6416_keypad_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tca6416_keys_platform_data *pdata;
struct tca6416_keypad_chip *chip;
struct input_dev *input;
int error;
int i;
/* Check functionality */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
dev_err(&client->dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
pdata = client->dev.platform_data;
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
return -EINVAL;
}
chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
pdata->nbuttons * sizeof(struct tca6416_button),
GFP_KERNEL);
input = input_allocate_device();
if (!chip || !input) {
error = -ENOMEM;
goto fail1;
}
chip->client = client;
chip->input = input;
chip->io_size = id->driver_data;
chip->pinmask = pdata->pinmask;
chip->use_polling = pdata->use_polling;
INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func);
input->phys = "tca6416-keys/input0";
input->name = client->name;
input->dev.parent = &client->dev;
input->open = tca6416_keys_open;
input->close = tca6416_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
unsigned int type;
chip->buttons[i] = pdata->buttons[i];
type = (pdata->buttons[i].type) ?: EV_KEY;
input_set_capability(input, type, pdata->buttons[i].code);
}
input_set_drvdata(input, chip);
/*
* Initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
error = tca6416_setup_registers(chip);
if (error)
goto fail1;
if (!chip->use_polling) {
if (pdata->irq_is_gpio)
chip->irqnum = gpio_to_irq(client->irq);
else
chip->irqnum = client->irq;
error = request_threaded_irq(chip->irqnum, NULL,
tca6416_keys_isr,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"tca6416-keypad", chip);
if (error) {
dev_dbg(&client->dev,
"Unable to claim irq %d; error %d\n",
chip->irqnum, error);
goto fail1;
}
disable_irq(chip->irqnum);
}
error = input_register_device(input);
if (error) {
dev_dbg(&client->dev,
"Unable to register input device, error: %d\n", error);
goto fail2;
}
i2c_set_clientdata(client, chip);
device_init_wakeup(&client->dev, 1);
return 0;
fail2:
if (!chip->use_polling) {
free_irq(chip->irqnum, chip);
enable_irq(chip->irqnum);
}
fail1:
input_free_device(input);
kfree(chip);
return error;
}
static int tca6416_keypad_remove(struct i2c_client *client)
{
struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
if (!chip->use_polling) {
free_irq(chip->irqnum, chip);
enable_irq(chip->irqnum);
}
input_unregister_device(chip->input);
kfree(chip);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tca6416_keypad_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev))
enable_irq_wake(chip->irqnum);
return 0;
}
static int tca6416_keypad_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev))
disable_irq_wake(chip->irqnum);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops,
tca6416_keypad_suspend, tca6416_keypad_resume);
static struct i2c_driver tca6416_keypad_driver = {
.driver = {
.name = "tca6416-keypad",
.pm = &tca6416_keypad_dev_pm_ops,
},
.probe = tca6416_keypad_probe,
.remove = tca6416_keypad_remove,
.id_table = tca6416_id,
};
static int __init tca6416_keypad_init(void)
{
return i2c_add_driver(&tca6416_keypad_driver);
}
subsys_initcall(tca6416_keypad_init);
static void __exit tca6416_keypad_exit(void)
{
i2c_del_driver(&tca6416_keypad_driver);
}
module_exit(tca6416_keypad_exit);
MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>");
MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,421 @@
/*
* Driver for TCA8418 I2C keyboard
*
* Copyright (C) 2011 Fuel7, Inc. All rights reserved.
*
* Author: Kyle Manna <kyle.manna@fuel7.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* 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 021110-1307, USA.
*
* If you can't comply with GPLv2, alternative licensing terms may be
* arranged. Please contact Fuel7, Inc. (http://fuel7.com/) for proprietary
* alternative licensing inquiries.
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/tca8418_keypad.h>
#include <linux/of.h>
/* TCA8418 hardware limits */
#define TCA8418_MAX_ROWS 8
#define TCA8418_MAX_COLS 10
/* TCA8418 register offsets */
#define REG_CFG 0x01
#define REG_INT_STAT 0x02
#define REG_KEY_LCK_EC 0x03
#define REG_KEY_EVENT_A 0x04
#define REG_KEY_EVENT_B 0x05
#define REG_KEY_EVENT_C 0x06
#define REG_KEY_EVENT_D 0x07
#define REG_KEY_EVENT_E 0x08
#define REG_KEY_EVENT_F 0x09
#define REG_KEY_EVENT_G 0x0A
#define REG_KEY_EVENT_H 0x0B
#define REG_KEY_EVENT_I 0x0C
#define REG_KEY_EVENT_J 0x0D
#define REG_KP_LCK_TIMER 0x0E
#define REG_UNLOCK1 0x0F
#define REG_UNLOCK2 0x10
#define REG_GPIO_INT_STAT1 0x11
#define REG_GPIO_INT_STAT2 0x12
#define REG_GPIO_INT_STAT3 0x13
#define REG_GPIO_DAT_STAT1 0x14
#define REG_GPIO_DAT_STAT2 0x15
#define REG_GPIO_DAT_STAT3 0x16
#define REG_GPIO_DAT_OUT1 0x17
#define REG_GPIO_DAT_OUT2 0x18
#define REG_GPIO_DAT_OUT3 0x19
#define REG_GPIO_INT_EN1 0x1A
#define REG_GPIO_INT_EN2 0x1B
#define REG_GPIO_INT_EN3 0x1C
#define REG_KP_GPIO1 0x1D
#define REG_KP_GPIO2 0x1E
#define REG_KP_GPIO3 0x1F
#define REG_GPI_EM1 0x20
#define REG_GPI_EM2 0x21
#define REG_GPI_EM3 0x22
#define REG_GPIO_DIR1 0x23
#define REG_GPIO_DIR2 0x24
#define REG_GPIO_DIR3 0x25
#define REG_GPIO_INT_LVL1 0x26
#define REG_GPIO_INT_LVL2 0x27
#define REG_GPIO_INT_LVL3 0x28
#define REG_DEBOUNCE_DIS1 0x29
#define REG_DEBOUNCE_DIS2 0x2A
#define REG_DEBOUNCE_DIS3 0x2B
#define REG_GPIO_PULL1 0x2C
#define REG_GPIO_PULL2 0x2D
#define REG_GPIO_PULL3 0x2E
/* TCA8418 bit definitions */
#define CFG_AI BIT(7)
#define CFG_GPI_E_CFG BIT(6)
#define CFG_OVR_FLOW_M BIT(5)
#define CFG_INT_CFG BIT(4)
#define CFG_OVR_FLOW_IEN BIT(3)
#define CFG_K_LCK_IEN BIT(2)
#define CFG_GPI_IEN BIT(1)
#define CFG_KE_IEN BIT(0)
#define INT_STAT_CAD_INT BIT(4)
#define INT_STAT_OVR_FLOW_INT BIT(3)
#define INT_STAT_K_LCK_INT BIT(2)
#define INT_STAT_GPI_INT BIT(1)
#define INT_STAT_K_INT BIT(0)
/* TCA8418 register masks */
#define KEY_LCK_EC_KEC 0x7
#define KEY_EVENT_CODE 0x7f
#define KEY_EVENT_VALUE 0x80
struct tca8418_keypad {
struct i2c_client *client;
struct input_dev *input;
unsigned int row_shift;
};
/*
* Write a byte to the TCA8418
*/
static int tca8418_write_byte(struct tca8418_keypad *keypad_data,
int reg, u8 val)
{
int error;
error = i2c_smbus_write_byte_data(keypad_data->client, reg, val);
if (error < 0) {
dev_err(&keypad_data->client->dev,
"%s failed, reg: %d, val: %d, error: %d\n",
__func__, reg, val, error);
return error;
}
return 0;
}
/*
* Read a byte from the TCA8418
*/
static int tca8418_read_byte(struct tca8418_keypad *keypad_data,
int reg, u8 *val)
{
int error;
error = i2c_smbus_read_byte_data(keypad_data->client, reg);
if (error < 0) {
dev_err(&keypad_data->client->dev,
"%s failed, reg: %d, error: %d\n",
__func__, reg, error);
return error;
}
*val = (u8)error;
return 0;
}
static void tca8418_read_keypad(struct tca8418_keypad *keypad_data)
{
struct input_dev *input = keypad_data->input;
unsigned short *keymap = input->keycode;
int error, col, row;
u8 reg, state, code;
/* Initial read of the key event FIFO */
error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
/* Assume that key code 0 signifies empty FIFO */
while (error >= 0 && reg > 0) {
state = reg & KEY_EVENT_VALUE;
code = reg & KEY_EVENT_CODE;
row = code / TCA8418_MAX_COLS;
col = code % TCA8418_MAX_COLS;
row = (col) ? row : row - 1;
col = (col) ? col - 1 : TCA8418_MAX_COLS - 1;
code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift);
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keymap[code], state);
/* Read for next loop */
error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
}
if (error < 0)
dev_err(&keypad_data->client->dev,
"unable to read REG_KEY_EVENT_A\n");
input_sync(input);
}
/*
* Threaded IRQ handler and this can (and will) sleep.
*/
static irqreturn_t tca8418_irq_handler(int irq, void *dev_id)
{
struct tca8418_keypad *keypad_data = dev_id;
u8 reg;
int error;
error = tca8418_read_byte(keypad_data, REG_INT_STAT, &reg);
if (error) {
dev_err(&keypad_data->client->dev,
"unable to read REG_INT_STAT\n");
return IRQ_NONE;
}
if (!reg)
return IRQ_NONE;
if (reg & INT_STAT_OVR_FLOW_INT)
dev_warn(&keypad_data->client->dev, "overflow occurred\n");
if (reg & INT_STAT_K_INT)
tca8418_read_keypad(keypad_data);
/* Clear all interrupts, even IRQs we didn't check (GPI, CAD, LCK) */
reg = 0xff;
error = tca8418_write_byte(keypad_data, REG_INT_STAT, reg);
if (error)
dev_err(&keypad_data->client->dev,
"unable to clear REG_INT_STAT\n");
return IRQ_HANDLED;
}
/*
* Configure the TCA8418 for keypad operation
*/
static int tca8418_configure(struct tca8418_keypad *keypad_data,
u32 rows, u32 cols)
{
int reg, error;
/* Write config register, if this fails assume device not present */
error = tca8418_write_byte(keypad_data, REG_CFG,
CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN);
if (error < 0)
return -ENODEV;
/* Assemble a mask for row and column registers */
reg = ~(~0 << rows);
reg += (~(~0 << cols)) << 8;
/* Set registers to keypad mode */
error |= tca8418_write_byte(keypad_data, REG_KP_GPIO1, reg);
error |= tca8418_write_byte(keypad_data, REG_KP_GPIO2, reg >> 8);
error |= tca8418_write_byte(keypad_data, REG_KP_GPIO3, reg >> 16);
/* Enable column debouncing */
error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS1, reg);
error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS2, reg >> 8);
error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS3, reg >> 16);
return error;
}
static int tca8418_keypad_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
const struct tca8418_keypad_platform_data *pdata =
dev_get_platdata(dev);
struct tca8418_keypad *keypad_data;
struct input_dev *input;
const struct matrix_keymap_data *keymap_data = NULL;
u32 rows = 0, cols = 0;
bool rep = false;
bool irq_is_gpio = false;
int irq;
int error, row_shift, max_keys;
/* Copy the platform data */
if (pdata) {
if (!pdata->keymap_data) {
dev_err(dev, "no keymap data defined\n");
return -EINVAL;
}
keymap_data = pdata->keymap_data;
rows = pdata->rows;
cols = pdata->cols;
rep = pdata->rep;
irq_is_gpio = pdata->irq_is_gpio;
} else {
struct device_node *np = dev->of_node;
int err;
err = matrix_keypad_parse_of_params(dev, &rows, &cols);
if (err)
return err;
rep = of_property_read_bool(np, "keypad,autorepeat");
}
if (!rows || rows > TCA8418_MAX_ROWS) {
dev_err(dev, "invalid rows\n");
return -EINVAL;
}
if (!cols || cols > TCA8418_MAX_COLS) {
dev_err(dev, "invalid columns\n");
return -EINVAL;
}
/* Check i2c driver capabilities */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
dev_err(dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
row_shift = get_count_order(cols);
max_keys = rows << row_shift;
/* Allocate memory for keypad_data and input device */
keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
if (!keypad_data)
return -ENOMEM;
keypad_data->client = client;
keypad_data->row_shift = row_shift;
/* Initialize the chip or fail if chip isn't present */
error = tca8418_configure(keypad_data, rows, cols);
if (error < 0)
return error;
/* Configure input device */
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
keypad_data->input = input;
input->name = client->name;
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x001;
input->id.version = 0x0001;
error = matrix_keypad_build_keymap(keymap_data, NULL, rows, cols,
NULL, input);
if (error) {
dev_err(dev, "Failed to build keymap\n");
return error;
}
if (rep)
__set_bit(EV_REP, input->evbit);
input_set_capability(input, EV_MSC, MSC_SCAN);
input_set_drvdata(input, keypad_data);
irq = client->irq;
if (irq_is_gpio)
irq = gpio_to_irq(irq);
error = devm_request_threaded_irq(dev, irq, NULL, tca8418_irq_handler,
IRQF_TRIGGER_FALLING |
IRQF_SHARED |
IRQF_ONESHOT,
client->name, keypad_data);
if (error) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
client->irq, error);
return error;
}
error = input_register_device(input);
if (error) {
dev_err(dev, "Unable to register input device, error: %d\n",
error);
return error;
}
return 0;
}
static const struct i2c_device_id tca8418_id[] = {
{ TCA8418_NAME, 8418, },
{ }
};
MODULE_DEVICE_TABLE(i2c, tca8418_id);
#ifdef CONFIG_OF
static const struct of_device_id tca8418_dt_ids[] = {
{ .compatible = "ti,tca8418", },
{ }
};
MODULE_DEVICE_TABLE(of, tca8418_dt_ids);
#endif
static struct i2c_driver tca8418_keypad_driver = {
.driver = {
.name = TCA8418_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tca8418_dt_ids),
},
.probe = tca8418_keypad_probe,
.id_table = tca8418_id,
};
static int __init tca8418_keypad_init(void)
{
return i2c_add_driver(&tca8418_keypad_driver);
}
subsys_initcall(tca8418_keypad_init);
static void __exit tca8418_keypad_exit(void)
{
i2c_del_driver(&tca8418_keypad_driver);
}
module_exit(tca8418_keypad_exit);
MODULE_AUTHOR("Kyle Manna <kyle.manna@fuel7.com>");
MODULE_DESCRIPTION("Keypad driver for TCA8418");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,841 @@
/*
* Keyboard class input driver for the NVIDIA Tegra SoC internal matrix
* keyboard controller
*
* Copyright (c) 2009-2011, NVIDIA Corporation.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/input/matrix_keypad.h>
#include <linux/clk/tegra.h>
#include <linux/err.h>
#define KBC_MAX_KPENT 8
/* Maximum row/column supported by Tegra KBC yet is 16x8 */
#define KBC_MAX_GPIO 24
/* Maximum keys supported by Tegra KBC yet is 16 x 8*/
#define KBC_MAX_KEY (16 * 8)
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu
/* KBC row scan time and delay for beginning the row scan. */
#define KBC_ROW_SCAN_TIME 16
#define KBC_ROW_SCAN_DLY 5
/* KBC uses a 32KHz clock so a cycle = 1/32Khz */
#define KBC_CYCLE_MS 32
/* KBC Registers */
/* KBC Control Register */
#define KBC_CONTROL_0 0x0
#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14)
#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4)
#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3)
#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1)
#define KBC_CONTROL_KBC_EN (1 << 0)
/* KBC Interrupt Register */
#define KBC_INT_0 0x4
#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2)
#define KBC_INT_KEYPRESS_INT_STATUS (1 << 0)
#define KBC_ROW_CFG0_0 0x8
#define KBC_COL_CFG0_0 0x18
#define KBC_TO_CNT_0 0x24
#define KBC_INIT_DLY_0 0x28
#define KBC_RPT_DLY_0 0x2c
#define KBC_KP_ENT0_0 0x30
#define KBC_KP_ENT1_0 0x34
#define KBC_ROW0_MASK_0 0x38
#define KBC_ROW_SHIFT 3
enum tegra_pin_type {
PIN_CFG_IGNORE,
PIN_CFG_COL,
PIN_CFG_ROW,
};
/* Tegra KBC hw support */
struct tegra_kbc_hw_support {
int max_rows;
int max_columns;
};
struct tegra_kbc_pin_cfg {
enum tegra_pin_type type;
unsigned char num;
};
struct tegra_kbc {
struct device *dev;
unsigned int debounce_cnt;
unsigned int repeat_cnt;
struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
const struct matrix_keymap_data *keymap_data;
bool wakeup;
void __iomem *mmio;
struct input_dev *idev;
int irq;
spinlock_t lock;
unsigned int repoll_dly;
unsigned long cp_dly_jiffies;
unsigned int cp_to_wkup_dly;
bool use_fn_map;
bool use_ghost_filter;
bool keypress_caused_wake;
unsigned short keycode[KBC_MAX_KEY * 2];
unsigned short current_keys[KBC_MAX_KPENT];
unsigned int num_pressed_keys;
u32 wakeup_key;
struct timer_list timer;
struct clk *clk;
const struct tegra_kbc_hw_support *hw_support;
int max_keys;
int num_rows_and_columns;
};
static void tegra_kbc_report_released_keys(struct input_dev *input,
unsigned short old_keycodes[],
unsigned int old_num_keys,
unsigned short new_keycodes[],
unsigned int new_num_keys)
{
unsigned int i, j;
for (i = 0; i < old_num_keys; i++) {
for (j = 0; j < new_num_keys; j++)
if (old_keycodes[i] == new_keycodes[j])
break;
if (j == new_num_keys)
input_report_key(input, old_keycodes[i], 0);
}
}
static void tegra_kbc_report_pressed_keys(struct input_dev *input,
unsigned char scancodes[],
unsigned short keycodes[],
unsigned int num_pressed_keys)
{
unsigned int i;
for (i = 0; i < num_pressed_keys; i++) {
input_event(input, EV_MSC, MSC_SCAN, scancodes[i]);
input_report_key(input, keycodes[i], 1);
}
}
static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
{
unsigned char scancodes[KBC_MAX_KPENT];
unsigned short keycodes[KBC_MAX_KPENT];
u32 val = 0;
unsigned int i;
unsigned int num_down = 0;
bool fn_keypress = false;
bool key_in_same_row = false;
bool key_in_same_col = false;
for (i = 0; i < KBC_MAX_KPENT; i++) {
if ((i % 4) == 0)
val = readl(kbc->mmio + KBC_KP_ENT0_0 + i);
if (val & 0x80) {
unsigned int col = val & 0x07;
unsigned int row = (val >> 3) & 0x0f;
unsigned char scancode =
MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT);
scancodes[num_down] = scancode;
keycodes[num_down] = kbc->keycode[scancode];
/* If driver uses Fn map, do not report the Fn key. */
if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map)
fn_keypress = true;
else
num_down++;
}
val >>= 8;
}
/*
* Matrix keyboard designs are prone to keyboard ghosting.
* Ghosting occurs if there are 3 keys such that -
* any 2 of the 3 keys share a row, and any 2 of them share a column.
* If so ignore the key presses for this iteration.
*/
if (kbc->use_ghost_filter && num_down >= 3) {
for (i = 0; i < num_down; i++) {
unsigned int j;
u8 curr_col = scancodes[i] & 0x07;
u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT;
/*
* Find 2 keys such that one key is in the same row
* and the other is in the same column as the i-th key.
*/
for (j = i + 1; j < num_down; j++) {
u8 col = scancodes[j] & 0x07;
u8 row = scancodes[j] >> KBC_ROW_SHIFT;
if (col == curr_col)
key_in_same_col = true;
if (row == curr_row)
key_in_same_row = true;
}
}
}
/*
* If the platform uses Fn keymaps, translate keys on a Fn keypress.
* Function keycodes are max_keys apart from the plain keycodes.
*/
if (fn_keypress) {
for (i = 0; i < num_down; i++) {
scancodes[i] += kbc->max_keys;
keycodes[i] = kbc->keycode[scancodes[i]];
}
}
/* Ignore the key presses for this iteration? */
if (key_in_same_col && key_in_same_row)
return;
tegra_kbc_report_released_keys(kbc->idev,
kbc->current_keys, kbc->num_pressed_keys,
keycodes, num_down);
tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down);
input_sync(kbc->idev);
memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys));
kbc->num_pressed_keys = num_down;
}
static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
{
u32 val;
val = readl(kbc->mmio + KBC_CONTROL_0);
if (enable)
val |= KBC_CONTROL_FIFO_CNT_INT_EN;
else
val &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
writel(val, kbc->mmio + KBC_CONTROL_0);
}
static void tegra_kbc_keypress_timer(unsigned long data)
{
struct tegra_kbc *kbc = (struct tegra_kbc *)data;
unsigned long flags;
u32 val;
unsigned int i;
spin_lock_irqsave(&kbc->lock, flags);
val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
if (val) {
unsigned long dly;
tegra_kbc_report_keys(kbc);
/*
* If more than one keys are pressed we need not wait
* for the repoll delay.
*/
dly = (val == 1) ? kbc->repoll_dly : 1;
mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly));
} else {
/* Release any pressed keys and exit the polling loop */
for (i = 0; i < kbc->num_pressed_keys; i++)
input_report_key(kbc->idev, kbc->current_keys[i], 0);
input_sync(kbc->idev);
kbc->num_pressed_keys = 0;
/* All keys are released so enable the keypress interrupt */
tegra_kbc_set_fifo_interrupt(kbc, true);
}
spin_unlock_irqrestore(&kbc->lock, flags);
}
static irqreturn_t tegra_kbc_isr(int irq, void *args)
{
struct tegra_kbc *kbc = args;
unsigned long flags;
u32 val;
spin_lock_irqsave(&kbc->lock, flags);
/*
* Quickly bail out & reenable interrupts if the fifo threshold
* count interrupt wasn't the interrupt source
*/
val = readl(kbc->mmio + KBC_INT_0);
writel(val, kbc->mmio + KBC_INT_0);
if (val & KBC_INT_FIFO_CNT_INT_STATUS) {
/*
* Until all keys are released, defer further processing to
* the polling loop in tegra_kbc_keypress_timer.
*/
tegra_kbc_set_fifo_interrupt(kbc, false);
mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies);
} else if (val & KBC_INT_KEYPRESS_INT_STATUS) {
/* We can be here only through system resume path */
kbc->keypress_caused_wake = true;
}
spin_unlock_irqrestore(&kbc->lock, flags);
return IRQ_HANDLED;
}
static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
{
int i;
unsigned int rst_val;
u32 val, addr;
/* Either mask all keys or none. */
rst_val = ~0;
for (i = 0; i < kbc->hw_support->max_rows; i++)
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
if (filter && kbc->wakeup) {
addr = KBC_ROW0_MASK_0;
val = readl(kbc->mmio + addr);
val &= ~1;
writel(val, kbc->mmio + addr);
}
}
static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
{
int i;
for (i = 0; i < KBC_MAX_GPIO; i++) {
u32 r_shft = 5 * (i % 6);
u32 c_shft = 4 * (i % 8);
u32 r_mask = 0x1f << r_shft;
u32 c_mask = 0x0f << c_shft;
u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0;
u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0;
u32 row_cfg = readl(kbc->mmio + r_offs);
u32 col_cfg = readl(kbc->mmio + c_offs);
row_cfg &= ~r_mask;
col_cfg &= ~c_mask;
switch (kbc->pin_cfg[i].type) {
case PIN_CFG_ROW:
row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft;
break;
case PIN_CFG_COL:
col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft;
break;
case PIN_CFG_IGNORE:
break;
}
writel(row_cfg, kbc->mmio + r_offs);
writel(col_cfg, kbc->mmio + c_offs);
}
}
static int tegra_kbc_start(struct tegra_kbc *kbc)
{
unsigned int debounce_cnt;
u32 val = 0;
clk_prepare_enable(kbc->clk);
/* Reset the KBC controller to clear all previous status.*/
tegra_periph_reset_assert(kbc->clk);
udelay(100);
tegra_periph_reset_deassert(kbc->clk);
udelay(100);
tegra_kbc_config_pins(kbc);
tegra_kbc_setup_wakekeys(kbc, false);
writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
/* Keyboard debounce count is maximum of 12 bits. */
debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
val |= KBC_CONTROL_KBC_EN; /* enable */
writel(val, kbc->mmio + KBC_CONTROL_0);
/*
* Compute the delay(ns) from interrupt mode to continuous polling
* mode so the timer routine is scheduled appropriately.
*/
val = readl(kbc->mmio + KBC_INIT_DLY_0);
kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32);
kbc->num_pressed_keys = 0;
/*
* Atomically clear out any remaining entries in the key FIFO
* and enable keyboard interrupts.
*/
while (1) {
val = readl(kbc->mmio + KBC_INT_0);
val >>= 4;
if (!val)
break;
val = readl(kbc->mmio + KBC_KP_ENT0_0);
val = readl(kbc->mmio + KBC_KP_ENT1_0);
}
writel(0x7, kbc->mmio + KBC_INT_0);
enable_irq(kbc->irq);
return 0;
}
static void tegra_kbc_stop(struct tegra_kbc *kbc)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&kbc->lock, flags);
val = readl(kbc->mmio + KBC_CONTROL_0);
val &= ~1;
writel(val, kbc->mmio + KBC_CONTROL_0);
spin_unlock_irqrestore(&kbc->lock, flags);
disable_irq(kbc->irq);
del_timer_sync(&kbc->timer);
clk_disable_unprepare(kbc->clk);
}
static int tegra_kbc_open(struct input_dev *dev)
{
struct tegra_kbc *kbc = input_get_drvdata(dev);
return tegra_kbc_start(kbc);
}
static void tegra_kbc_close(struct input_dev *dev)
{
struct tegra_kbc *kbc = input_get_drvdata(dev);
return tegra_kbc_stop(kbc);
}
static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
unsigned int *num_rows)
{
int i;
*num_rows = 0;
for (i = 0; i < KBC_MAX_GPIO; i++) {
const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i];
switch (pin_cfg->type) {
case PIN_CFG_ROW:
if (pin_cfg->num >= kbc->hw_support->max_rows) {
dev_err(kbc->dev,
"pin_cfg[%d]: invalid row number %d\n",
i, pin_cfg->num);
return false;
}
(*num_rows)++;
break;
case PIN_CFG_COL:
if (pin_cfg->num >= kbc->hw_support->max_columns) {
dev_err(kbc->dev,
"pin_cfg[%d]: invalid column number %d\n",
i, pin_cfg->num);
return false;
}
break;
case PIN_CFG_IGNORE:
break;
default:
dev_err(kbc->dev,
"pin_cfg[%d]: invalid entry type %d\n",
pin_cfg->type, pin_cfg->num);
return false;
}
}
return true;
}
static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
{
struct device_node *np = kbc->dev->of_node;
u32 prop;
int i;
u32 num_rows = 0;
u32 num_cols = 0;
u32 cols_cfg[KBC_MAX_GPIO];
u32 rows_cfg[KBC_MAX_GPIO];
int proplen;
int ret;
if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
kbc->debounce_cnt = prop;
if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
kbc->repeat_cnt = prop;
if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
kbc->use_ghost_filter = true;
if (of_find_property(np, "nvidia,wakeup-source", NULL))
kbc->wakeup = true;
if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
return -ENOENT;
}
num_rows = proplen / sizeof(u32);
if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) {
dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n");
return -ENOENT;
}
num_cols = proplen / sizeof(u32);
if (num_rows > kbc->hw_support->max_rows) {
dev_err(kbc->dev,
"Number of rows is more than supported by hardware\n");
return -EINVAL;
}
if (num_cols > kbc->hw_support->max_columns) {
dev_err(kbc->dev,
"Number of cols is more than supported by hardware\n");
return -EINVAL;
}
if (!of_get_property(np, "linux,keymap", &proplen)) {
dev_err(kbc->dev, "property linux,keymap not found\n");
return -ENOENT;
}
if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
dev_err(kbc->dev,
"keypad rows/columns not porperly specified\n");
return -EINVAL;
}
/* Set all pins as non-configured */
for (i = 0; i < kbc->num_rows_and_columns; i++)
kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
rows_cfg, num_rows);
if (ret < 0) {
dev_err(kbc->dev, "Rows configurations are not proper\n");
return -EINVAL;
}
ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins",
cols_cfg, num_cols);
if (ret < 0) {
dev_err(kbc->dev, "Cols configurations are not proper\n");
return -EINVAL;
}
for (i = 0; i < num_rows; i++) {
kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW;
kbc->pin_cfg[rows_cfg[i]].num = i;
}
for (i = 0; i < num_cols; i++) {
kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL;
kbc->pin_cfg[cols_cfg[i]].num = i;
}
return 0;
}
static const struct tegra_kbc_hw_support tegra20_kbc_hw_support = {
.max_rows = 16,
.max_columns = 8,
};
static const struct tegra_kbc_hw_support tegra11_kbc_hw_support = {
.max_rows = 11,
.max_columns = 8,
};
static const struct of_device_id tegra_kbc_of_match[] = {
{ .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support},
{ .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support},
{ .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support},
{ },
};
MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
static int tegra_kbc_probe(struct platform_device *pdev)
{
struct tegra_kbc *kbc;
struct resource *res;
int err;
int num_rows = 0;
unsigned int debounce_cnt;
unsigned int scan_time_rows;
unsigned int keymap_rows;
const struct of_device_id *match;
match = of_match_device(of_match_ptr(tegra_kbc_of_match), &pdev->dev);
kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
if (!kbc) {
dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
return -ENOMEM;
}
kbc->dev = &pdev->dev;
kbc->hw_support = match->data;
kbc->max_keys = kbc->hw_support->max_rows *
kbc->hw_support->max_columns;
kbc->num_rows_and_columns = kbc->hw_support->max_rows +
kbc->hw_support->max_columns;
keymap_rows = kbc->max_keys;
spin_lock_init(&kbc->lock);
err = tegra_kbc_parse_dt(kbc);
if (err)
return err;
if (!tegra_kbc_check_pin_cfg(kbc, &num_rows))
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
return -ENXIO;
}
kbc->irq = platform_get_irq(pdev, 0);
if (kbc->irq < 0) {
dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
return -ENXIO;
}
kbc->idev = devm_input_allocate_device(&pdev->dev);
if (!kbc->idev) {
dev_err(&pdev->dev, "failed to allocate input device\n");
return -ENOMEM;
}
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
kbc->mmio = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(kbc->mmio))
return PTR_ERR(kbc->mmio);
kbc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbc->clk)) {
dev_err(&pdev->dev, "failed to get keyboard clock\n");
return PTR_ERR(kbc->clk);
}
/*
* The time delay between two consecutive reads of the FIFO is
* the sum of the repeat time and the time taken for scanning
* the rows. There is an additional delay before the row scanning
* starts. The repoll delay is computed in milliseconds.
*/
debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt;
kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
kbc->idev->name = pdev->name;
kbc->idev->id.bustype = BUS_HOST;
kbc->idev->dev.parent = &pdev->dev;
kbc->idev->open = tegra_kbc_open;
kbc->idev->close = tegra_kbc_close;
if (kbc->keymap_data && kbc->use_fn_map)
keymap_rows *= 2;
err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
keymap_rows,
kbc->hw_support->max_columns,
kbc->keycode, kbc->idev);
if (err) {
dev_err(&pdev->dev, "failed to setup keymap\n");
return err;
}
__set_bit(EV_REP, kbc->idev->evbit);
input_set_capability(kbc->idev, EV_MSC, MSC_SCAN);
input_set_drvdata(kbc->idev, kbc);
err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
if (err) {
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
return err;
}
disable_irq(kbc->irq);
err = input_register_device(kbc->idev);
if (err) {
dev_err(&pdev->dev, "failed to register input device\n");
return err;
}
platform_set_drvdata(pdev, kbc);
device_init_wakeup(&pdev->dev, kbc->wakeup);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
{
u32 val;
val = readl(kbc->mmio + KBC_CONTROL_0);
if (enable)
val |= KBC_CONTROL_KEYPRESS_INT_EN;
else
val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
writel(val, kbc->mmio + KBC_CONTROL_0);
}
static int tegra_kbc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
mutex_lock(&kbc->idev->mutex);
if (device_may_wakeup(&pdev->dev)) {
disable_irq(kbc->irq);
del_timer_sync(&kbc->timer);
tegra_kbc_set_fifo_interrupt(kbc, false);
/* Forcefully clear the interrupt status */
writel(0x7, kbc->mmio + KBC_INT_0);
/*
* Store the previous resident time of continuous polling mode.
* Force the keyboard into interrupt mode.
*/
kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0);
writel(0, kbc->mmio + KBC_TO_CNT_0);
tegra_kbc_setup_wakekeys(kbc, true);
msleep(30);
kbc->keypress_caused_wake = false;
/* Enable keypress interrupt before going into suspend. */
tegra_kbc_set_keypress_interrupt(kbc, true);
enable_irq(kbc->irq);
enable_irq_wake(kbc->irq);
} else {
if (kbc->idev->users)
tegra_kbc_stop(kbc);
}
mutex_unlock(&kbc->idev->mutex);
return 0;
}
static int tegra_kbc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
int err = 0;
mutex_lock(&kbc->idev->mutex);
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(kbc->irq);
tegra_kbc_setup_wakekeys(kbc, false);
/* We will use fifo interrupts for key detection. */
tegra_kbc_set_keypress_interrupt(kbc, false);
/* Restore the resident time of continuous polling mode. */
writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0);
tegra_kbc_set_fifo_interrupt(kbc, true);
if (kbc->keypress_caused_wake && kbc->wakeup_key) {
/*
* We can't report events directly from the ISR
* because timekeeping is stopped when processing
* wakeup request and we get a nasty warning when
* we try to call do_gettimeofday() in evdev
* handler.
*/
input_report_key(kbc->idev, kbc->wakeup_key, 1);
input_sync(kbc->idev);
input_report_key(kbc->idev, kbc->wakeup_key, 0);
input_sync(kbc->idev);
}
} else {
if (kbc->idev->users)
err = tegra_kbc_start(kbc);
}
mutex_unlock(&kbc->idev->mutex);
return err;
}
#endif
static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume);
static struct platform_driver tegra_kbc_driver = {
.probe = tegra_kbc_probe,
.driver = {
.name = "tegra-kbc",
.owner = THIS_MODULE,
.pm = &tegra_kbc_pm_ops,
.of_match_table = tegra_kbc_of_match,
},
};
module_platform_driver(tegra_kbc_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>");
MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");
MODULE_ALIAS("platform:tegra-kbc");

View File

@@ -0,0 +1,331 @@
/*
* Texas Instruments TNETV107X Keypad Driver
*
* Copyright (C) 2010 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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/input/matrix_keypad.h>
#include <linux/module.h>
#define BITS(x) (BIT(x) - 1)
#define KEYPAD_ROWS 9
#define KEYPAD_COLS 9
#define DEBOUNCE_MIN 0x400ul
#define DEBOUNCE_MAX 0x3ffffffful
struct keypad_regs {
u32 rev;
u32 mode;
u32 mask;
u32 pol;
u32 dclock;
u32 rclock;
u32 stable_cnt;
u32 in_en;
u32 out;
u32 out_en;
u32 in;
u32 lock;
u32 pres[3];
};
#define keypad_read(kp, reg) __raw_readl(&(kp)->regs->reg)
#define keypad_write(kp, reg, val) __raw_writel(val, &(kp)->regs->reg)
struct keypad_data {
struct input_dev *input_dev;
struct resource *res;
struct keypad_regs __iomem *regs;
struct clk *clk;
struct device *dev;
spinlock_t lock;
u32 irq_press;
u32 irq_release;
int rows, cols, row_shift;
int debounce_ms, active_low;
u32 prev_keys[3];
unsigned short keycodes[];
};
static irqreturn_t keypad_irq(int irq, void *data)
{
struct keypad_data *kp = data;
int i, bit, val, row, col, code;
unsigned long flags;
u32 curr_keys[3];
u32 change;
spin_lock_irqsave(&kp->lock, flags);
memset(curr_keys, 0, sizeof(curr_keys));
if (irq == kp->irq_press)
for (i = 0; i < 3; i++)
curr_keys[i] = keypad_read(kp, pres[i]);
for (i = 0; i < 3; i++) {
change = curr_keys[i] ^ kp->prev_keys[i];
while (change) {
bit = fls(change) - 1;
change ^= BIT(bit);
val = curr_keys[i] & BIT(bit);
bit += i * 32;
row = bit / KEYPAD_COLS;
col = bit % KEYPAD_COLS;
code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
input_event(kp->input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(kp->input_dev, kp->keycodes[code],
val);
}
}
input_sync(kp->input_dev);
memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys));
if (irq == kp->irq_press)
keypad_write(kp, lock, 0); /* Allow hardware updates */
spin_unlock_irqrestore(&kp->lock, flags);
return IRQ_HANDLED;
}
static int keypad_start(struct input_dev *dev)
{
struct keypad_data *kp = input_get_drvdata(dev);
unsigned long mask, debounce, clk_rate_khz;
unsigned long flags;
clk_enable(kp->clk);
clk_rate_khz = clk_get_rate(kp->clk) / 1000;
spin_lock_irqsave(&kp->lock, flags);
/* Initialize device registers */
keypad_write(kp, mode, 0);
mask = BITS(kp->rows) << KEYPAD_COLS;
mask |= BITS(kp->cols);
keypad_write(kp, mask, ~mask);
keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff);
keypad_write(kp, stable_cnt, 3);
debounce = kp->debounce_ms * clk_rate_khz;
debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX);
keypad_write(kp, dclock, debounce);
keypad_write(kp, rclock, 4 * debounce);
keypad_write(kp, in_en, 1);
spin_unlock_irqrestore(&kp->lock, flags);
return 0;
}
static void keypad_stop(struct input_dev *dev)
{
struct keypad_data *kp = input_get_drvdata(dev);
synchronize_irq(kp->irq_press);
synchronize_irq(kp->irq_release);
clk_disable(kp->clk);
}
static int keypad_probe(struct platform_device *pdev)
{
const struct matrix_keypad_platform_data *pdata;
const struct matrix_keymap_data *keymap_data;
struct device *dev = &pdev->dev;
struct keypad_data *kp;
int error = 0, sz, row_shift;
u32 rev = 0;
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(dev, "cannot find device data\n");
return -EINVAL;
}
keymap_data = pdata->keymap_data;
if (!keymap_data) {
dev_err(dev, "cannot find keymap data\n");
return -EINVAL;
}
row_shift = get_count_order(pdata->num_col_gpios);
sz = offsetof(struct keypad_data, keycodes);
sz += (pdata->num_row_gpios << row_shift) * sizeof(kp->keycodes[0]);
kp = kzalloc(sz, GFP_KERNEL);
if (!kp) {
dev_err(dev, "cannot allocate device info\n");
return -ENOMEM;
}
kp->dev = dev;
kp->rows = pdata->num_row_gpios;
kp->cols = pdata->num_col_gpios;
kp->row_shift = row_shift;
platform_set_drvdata(pdev, kp);
spin_lock_init(&kp->lock);
kp->irq_press = platform_get_irq_byname(pdev, "press");
kp->irq_release = platform_get_irq_byname(pdev, "release");
if (kp->irq_press < 0 || kp->irq_release < 0) {
dev_err(dev, "cannot determine device interrupts\n");
error = -ENODEV;
goto error_res;
}
kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!kp->res) {
dev_err(dev, "cannot determine register area\n");
error = -ENODEV;
goto error_res;
}
if (!request_mem_region(kp->res->start, resource_size(kp->res),
pdev->name)) {
dev_err(dev, "cannot claim register memory\n");
kp->res = NULL;
error = -EINVAL;
goto error_res;
}
kp->regs = ioremap(kp->res->start, resource_size(kp->res));
if (!kp->regs) {
dev_err(dev, "cannot map register memory\n");
error = -ENOMEM;
goto error_map;
}
kp->clk = clk_get(dev, NULL);
if (IS_ERR(kp->clk)) {
dev_err(dev, "cannot claim device clock\n");
error = PTR_ERR(kp->clk);
goto error_clk;
}
error = request_threaded_irq(kp->irq_press, NULL, keypad_irq,
IRQF_ONESHOT, dev_name(dev), kp);
if (error < 0) {
dev_err(kp->dev, "Could not allocate keypad press key irq\n");
goto error_irq_press;
}
error = request_threaded_irq(kp->irq_release, NULL, keypad_irq,
IRQF_ONESHOT, dev_name(dev), kp);
if (error < 0) {
dev_err(kp->dev, "Could not allocate keypad release key irq\n");
goto error_irq_release;
}
kp->input_dev = input_allocate_device();
if (!kp->input_dev) {
dev_err(dev, "cannot allocate input device\n");
error = -ENOMEM;
goto error_input;
}
kp->input_dev->name = pdev->name;
kp->input_dev->dev.parent = &pdev->dev;
kp->input_dev->open = keypad_start;
kp->input_dev->close = keypad_stop;
clk_enable(kp->clk);
rev = keypad_read(kp, rev);
kp->input_dev->id.bustype = BUS_HOST;
kp->input_dev->id.product = ((rev >> 8) & 0x07);
kp->input_dev->id.version = ((rev >> 16) & 0xfff);
clk_disable(kp->clk);
error = matrix_keypad_build_keymap(keymap_data, NULL,
kp->rows, kp->cols,
kp->keycodes, kp->input_dev);
if (error) {
dev_err(dev, "Failed to build keymap\n");
goto error_reg;
}
if (!pdata->no_autorepeat)
kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(kp->input_dev, kp);
error = input_register_device(kp->input_dev);
if (error < 0) {
dev_err(dev, "Could not register input device\n");
goto error_reg;
}
return 0;
error_reg:
input_free_device(kp->input_dev);
error_input:
free_irq(kp->irq_release, kp);
error_irq_release:
free_irq(kp->irq_press, kp);
error_irq_press:
clk_put(kp->clk);
error_clk:
iounmap(kp->regs);
error_map:
release_mem_region(kp->res->start, resource_size(kp->res));
error_res:
platform_set_drvdata(pdev, NULL);
kfree(kp);
return error;
}
static int keypad_remove(struct platform_device *pdev)
{
struct keypad_data *kp = platform_get_drvdata(pdev);
free_irq(kp->irq_press, kp);
free_irq(kp->irq_release, kp);
input_unregister_device(kp->input_dev);
clk_put(kp->clk);
iounmap(kp->regs);
release_mem_region(kp->res->start, resource_size(kp->res));
platform_set_drvdata(pdev, NULL);
kfree(kp);
return 0;
}
static struct platform_driver keypad_driver = {
.probe = keypad_probe,
.remove = keypad_remove,
.driver.name = "tnetv107x-keypad",
.driver.owner = THIS_MODULE,
};
module_platform_driver(keypad_driver);
MODULE_AUTHOR("Cyril Chemparathy");
MODULE_DESCRIPTION("TNETV107X Keypad Driver");
MODULE_ALIAS("platform:tnetv107x-keypad");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,466 @@
/*
* twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
*
* Copyright (C) 2007 Texas Instruments, Inc.
* Copyright (C) 2008 Nokia Corporation
*
* Code re-written for 2430SDP by:
* Syed Mohammed Khasim <x0khasim@ti.com>
*
* Initial Code:
* Manjunatha G K <manjugk@ti.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
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl.h>
#include <linux/slab.h>
/*
* The TWL4030 family chips include a keypad controller that supports
* up to an 8x8 switch matrix. The controller can issue system wakeup
* events, since it uses only the always-on 32KiHz oscillator, and has
* an internal state machine that decodes pressed keys, including
* multi-key combinations.
*
* This driver lets boards define what keycodes they wish to report for
* which scancodes, as part of the "struct twl4030_keypad_data" used in
* the probe() routine.
*
* See the TPS65950 documentation; that's the general availability
* version of the TWL5030 second generation part.
*/
#define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */
#define TWL4030_MAX_COLS 8
/*
* Note that we add space for an extra column so that we can handle
* row lines connected to the gnd (see twl4030_col_xlate()).
*/
#define TWL4030_ROW_SHIFT 4
#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS << TWL4030_ROW_SHIFT)
struct twl4030_keypad {
unsigned short keymap[TWL4030_KEYMAP_SIZE];
u16 kp_state[TWL4030_MAX_ROWS];
unsigned n_rows;
unsigned n_cols;
unsigned irq;
struct device *dbg_dev;
struct input_dev *input;
};
/*----------------------------------------------------------------------*/
/* arbitrary prescaler value 0..7 */
#define PTV_PRESCALER 4
/* Register Offsets */
#define KEYP_CTRL 0x00
#define KEYP_DEB 0x01
#define KEYP_LONG_KEY 0x02
#define KEYP_LK_PTV 0x03
#define KEYP_TIMEOUT_L 0x04
#define KEYP_TIMEOUT_H 0x05
#define KEYP_KBC 0x06
#define KEYP_KBR 0x07
#define KEYP_SMS 0x08
#define KEYP_FULL_CODE_7_0 0x09 /* row 0 column status */
#define KEYP_FULL_CODE_15_8 0x0a /* ... row 1 ... */
#define KEYP_FULL_CODE_23_16 0x0b
#define KEYP_FULL_CODE_31_24 0x0c
#define KEYP_FULL_CODE_39_32 0x0d
#define KEYP_FULL_CODE_47_40 0x0e
#define KEYP_FULL_CODE_55_48 0x0f
#define KEYP_FULL_CODE_63_56 0x10
#define KEYP_ISR1 0x11
#define KEYP_IMR1 0x12
#define KEYP_ISR2 0x13
#define KEYP_IMR2 0x14
#define KEYP_SIR 0x15
#define KEYP_EDR 0x16 /* edge triggers */
#define KEYP_SIH_CTRL 0x17
/* KEYP_CTRL_REG Fields */
#define KEYP_CTRL_SOFT_NRST BIT(0)
#define KEYP_CTRL_SOFTMODEN BIT(1)
#define KEYP_CTRL_LK_EN BIT(2)
#define KEYP_CTRL_TOE_EN BIT(3)
#define KEYP_CTRL_TOLE_EN BIT(4)
#define KEYP_CTRL_RP_EN BIT(5)
#define KEYP_CTRL_KBD_ON BIT(6)
/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
#define KEYP_PERIOD_US(t, prescale) ((t) / (31 << (prescale + 1)) - 1)
/* KEYP_LK_PTV_REG Fields */
#define KEYP_LK_PTV_PTV_SHIFT 5
/* KEYP_{IMR,ISR,SIR} Fields */
#define KEYP_IMR1_MIS BIT(3)
#define KEYP_IMR1_TO BIT(2)
#define KEYP_IMR1_LK BIT(1)
#define KEYP_IMR1_KP BIT(0)
/* KEYP_EDR Fields */
#define KEYP_EDR_KP_FALLING 0x01
#define KEYP_EDR_KP_RISING 0x02
#define KEYP_EDR_KP_BOTH 0x03
#define KEYP_EDR_LK_FALLING 0x04
#define KEYP_EDR_LK_RISING 0x08
#define KEYP_EDR_TO_FALLING 0x10
#define KEYP_EDR_TO_RISING 0x20
#define KEYP_EDR_MIS_FALLING 0x40
#define KEYP_EDR_MIS_RISING 0x80
/*----------------------------------------------------------------------*/
static int twl4030_kpread(struct twl4030_keypad *kp,
u8 *data, u32 reg, u8 num_bytes)
{
int ret = twl_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
if (ret < 0)
dev_warn(kp->dbg_dev,
"Couldn't read TWL4030: %X - ret %d[%x]\n",
reg, ret, ret);
return ret;
}
static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
{
int ret = twl_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
if (ret < 0)
dev_warn(kp->dbg_dev,
"Could not write TWL4030: %X - ret %d[%x]\n",
reg, ret, ret);
return ret;
}
static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
{
/* If all bits in a row are active for all coloumns then
* we have that row line connected to gnd. Mark this
* key on as if it was on matrix position n_cols (ie
* one higher than the size of the matrix).
*/
if (col == 0xFF)
return 1 << kp->n_cols;
else
return col & ((1 << kp->n_cols) - 1);
}
static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
{
u8 new_state[TWL4030_MAX_ROWS];
int row;
int ret = twl4030_kpread(kp, new_state,
KEYP_FULL_CODE_7_0, kp->n_rows);
if (ret >= 0)
for (row = 0; row < kp->n_rows; row++)
state[row] = twl4030_col_xlate(kp, new_state[row]);
return ret;
}
static bool twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
{
int i;
u16 check = 0;
for (i = 0; i < kp->n_rows; i++) {
u16 col = key_state[i];
if ((col & check) && hweight16(col) > 1)
return true;
check |= col;
}
return false;
}
static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
{
struct input_dev *input = kp->input;
u16 new_state[TWL4030_MAX_ROWS];
int col, row;
if (release_all)
memset(new_state, 0, sizeof(new_state));
else {
/* check for any changes */
int ret = twl4030_read_kp_matrix_state(kp, new_state);
if (ret < 0) /* panic ... */
return;
if (twl4030_is_in_ghost_state(kp, new_state))
return;
}
/* check for changes and print those */
for (row = 0; row < kp->n_rows; row++) {
int changed = new_state[row] ^ kp->kp_state[row];
if (!changed)
continue;
/* Extra column handles "all gnd" rows */
for (col = 0; col < kp->n_cols + 1; col++) {
int code;
if (!(changed & (1 << col)))
continue;
dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
(new_state[row] & (1 << col)) ?
"press" : "release");
code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT);
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, kp->keymap[code],
new_state[row] & (1 << col));
}
kp->kp_state[row] = new_state[row];
}
input_sync(input);
}
/*
* Keypad interrupt handler
*/
static irqreturn_t do_kp_irq(int irq, void *_kp)
{
struct twl4030_keypad *kp = _kp;
u8 reg;
int ret;
/* Read & Clear TWL4030 pending interrupt */
ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
/* Release all keys if I2C has gone bad or
* the KEYP has gone to idle state */
if (ret >= 0 && (reg & KEYP_IMR1_KP))
twl4030_kp_scan(kp, false);
else
twl4030_kp_scan(kp, true);
return IRQ_HANDLED;
}
static int twl4030_kp_program(struct twl4030_keypad *kp)
{
u8 reg;
int i;
/* Enable controller, with hardware decoding but not autorepeat */
reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
| KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0)
return -EIO;
/* NOTE: we could use sih_setup() here to package keypad
* event sources as four different IRQs ... but we don't.
*/
/* Enable TO rising and KP rising and falling edge detection */
reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0)
return -EIO;
/* Set PTV prescaler Field */
reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0)
return -EIO;
/* Set key debounce time to 20 ms */
i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0)
return -EIO;
/* Set timeout period to 200 ms */
i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0)
return -EIO;
if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0)
return -EIO;
/*
* Enable Clear-on-Read; disable remembering events that fire
* after the IRQ but before our handler acks (reads) them,
*/
reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0)
return -EIO;
/* initialize key state; irqs update it from here on */
if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0)
return -EIO;
return 0;
}
/*
* Registers keypad device with input subsystem
* and configures TWL4030 keypad registers
*/
static int twl4030_kp_probe(struct platform_device *pdev)
{
struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
const struct matrix_keymap_data *keymap_data;
struct twl4030_keypad *kp;
struct input_dev *input;
u8 reg;
int error;
if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap_data ||
pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
dev_err(&pdev->dev, "Invalid platform_data\n");
return -EINVAL;
}
keymap_data = pdata->keymap_data;
kp = kzalloc(sizeof(*kp), GFP_KERNEL);
input = input_allocate_device();
if (!kp || !input) {
error = -ENOMEM;
goto err1;
}
/* Get the debug Device */
kp->dbg_dev = &pdev->dev;
kp->input = input;
kp->n_rows = pdata->rows;
kp->n_cols = pdata->cols;
kp->irq = platform_get_irq(pdev, 0);
/* setup input device */
input->name = "TWL4030 Keypad";
input->phys = "twl4030_keypad/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0003;
error = matrix_keypad_build_keymap(keymap_data, NULL,
TWL4030_MAX_ROWS,
1 << TWL4030_ROW_SHIFT,
kp->keymap, input);
if (error) {
dev_err(kp->dbg_dev, "Failed to build keymap\n");
goto err1;
}
input_set_capability(input, EV_MSC, MSC_SCAN);
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
error = input_register_device(input);
if (error) {
dev_err(kp->dbg_dev,
"Unable to register twl4030 keypad device\n");
goto err1;
}
error = twl4030_kp_program(kp);
if (error)
goto err2;
/*
* This ISR will always execute in kernel thread context because of
* the need to access the TWL4030 over the I2C bus.
*
* NOTE: we assume this host is wired to TWL4040 INT1, not INT2 ...
*/
error = request_threaded_irq(kp->irq, NULL, do_kp_irq,
0, pdev->name, kp);
if (error) {
dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
kp->irq);
goto err2;
}
/* Enable KP and TO interrupts now. */
reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
error = -EIO;
goto err3;
}
platform_set_drvdata(pdev, kp);
return 0;
err3:
/* mask all events - we don't care about the result */
(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
free_irq(kp->irq, NULL);
err2:
input_unregister_device(input);
input = NULL;
err1:
input_free_device(input);
kfree(kp);
return error;
}
static int twl4030_kp_remove(struct platform_device *pdev)
{
struct twl4030_keypad *kp = platform_get_drvdata(pdev);
free_irq(kp->irq, kp);
input_unregister_device(kp->input);
platform_set_drvdata(pdev, NULL);
kfree(kp);
return 0;
}
/*
* NOTE: twl4030 are multi-function devices connected via I2C.
* So this device is a child of an I2C parent, thus it needs to
* support unplug/replug (which most platform devices don't).
*/
static struct platform_driver twl4030_kp_driver = {
.probe = twl4030_kp_probe,
.remove = twl4030_kp_remove,
.driver = {
.name = "twl4030_keypad",
.owner = THIS_MODULE,
},
};
module_platform_driver(twl4030_kp_driver);
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("TWL4030 Keypad Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:twl4030_keypad");

View File

@@ -0,0 +1,271 @@
/*
* Copyright (c) 2008-2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/platform_data/keypad-w90p910.h>
/* Keypad Interface Control Registers */
#define KPI_CONF 0x00
#define KPI_3KCONF 0x04
#define KPI_LPCONF 0x08
#define KPI_STATUS 0x0C
#define IS1KEY (0x01 << 16)
#define INTTR (0x01 << 21)
#define KEY0R (0x0f << 3)
#define KEY0C 0x07
#define DEBOUNCE_BIT 0x08
#define KSIZE0 (0x01 << 16)
#define KSIZE1 (0x01 << 17)
#define KPSEL (0x01 << 19)
#define ENKP (0x01 << 18)
#define KGET_RAW(n) (((n) & KEY0R) >> 3)
#define KGET_COLUMN(n) ((n) & KEY0C)
#define W90P910_NUM_ROWS 8
#define W90P910_NUM_COLS 8
#define W90P910_ROW_SHIFT 3
struct w90p910_keypad {
const struct w90p910_keypad_platform_data *pdata;
struct clk *clk;
struct input_dev *input_dev;
void __iomem *mmio_base;
int irq;
unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS];
};
static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad,
unsigned int status)
{
struct input_dev *input_dev = keypad->input_dev;
unsigned int row = KGET_RAW(status);
unsigned int col = KGET_COLUMN(status);
unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT);
unsigned int key = keypad->keymap[code];
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, key, 1);
input_sync(input_dev);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, key, 0);
input_sync(input_dev);
}
static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id)
{
struct w90p910_keypad *keypad = dev_id;
unsigned int kstatus, val;
kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS);
val = INTTR | IS1KEY;
if (kstatus & val)
w90p910_keypad_scan_matrix(keypad, kstatus);
return IRQ_HANDLED;
}
static int w90p910_keypad_open(struct input_dev *dev)
{
struct w90p910_keypad *keypad = input_get_drvdata(dev);
const struct w90p910_keypad_platform_data *pdata = keypad->pdata;
unsigned int val, config;
/* Enable unit clock */
clk_enable(keypad->clk);
val = __raw_readl(keypad->mmio_base + KPI_CONF);
val |= (KPSEL | ENKP);
val &= ~(KSIZE0 | KSIZE1);
config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT);
val |= config;
__raw_writel(val, keypad->mmio_base + KPI_CONF);
return 0;
}
static void w90p910_keypad_close(struct input_dev *dev)
{
struct w90p910_keypad *keypad = input_get_drvdata(dev);
/* Disable clock unit */
clk_disable(keypad->clk);
}
static int w90p910_keypad_probe(struct platform_device *pdev)
{
const struct w90p910_keypad_platform_data *pdata =
pdev->dev.platform_data;
const struct matrix_keymap_data *keymap_data;
struct w90p910_keypad *keypad;
struct input_dev *input_dev;
struct resource *res;
int irq;
int error;
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
return -EINVAL;
}
keymap_data = pdata->keymap_data;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get keypad irq\n");
return -ENXIO;
}
keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL);
input_dev = input_allocate_device();
if (!keypad || !input_dev) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
error = -ENOMEM;
goto failed_free;
}
keypad->pdata = pdata;
keypad->input_dev = input_dev;
keypad->irq = irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
error = -ENXIO;
goto failed_free;
}
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (res == NULL) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto failed_free;
}
keypad->mmio_base = ioremap(res->start, resource_size(res));
if (keypad->mmio_base == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -ENXIO;
goto failed_free_res;
}
keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
error = PTR_ERR(keypad->clk);
goto failed_free_io;
}
/* set multi-function pin for w90p910 kpi. */
mfp_set_groupi(&pdev->dev);
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = w90p910_keypad_open;
input_dev->close = w90p910_keypad_close;
input_dev->dev.parent = &pdev->dev;
error = matrix_keypad_build_keymap(keymap_data, NULL,
W90P910_NUM_ROWS, W90P910_NUM_COLS,
keypad->keymap, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto failed_put_clk;
}
error = request_irq(keypad->irq, w90p910_keypad_irq_handler,
0, pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto failed_put_clk;
}
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
/* Register the input device */
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
goto failed_free_irq;
}
platform_set_drvdata(pdev, keypad);
return 0;
failed_free_irq:
free_irq(irq, pdev);
failed_put_clk:
clk_put(keypad->clk);
failed_free_io:
iounmap(keypad->mmio_base);
failed_free_res:
release_mem_region(res->start, resource_size(res));
failed_free:
input_free_device(input_dev);
kfree(keypad);
return error;
}
static int w90p910_keypad_remove(struct platform_device *pdev)
{
struct w90p910_keypad *keypad = platform_get_drvdata(pdev);
struct resource *res;
free_irq(keypad->irq, pdev);
clk_put(keypad->clk);
input_unregister_device(keypad->input_dev);
iounmap(keypad->mmio_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
kfree(keypad);
return 0;
}
static struct platform_driver w90p910_keypad_driver = {
.probe = w90p910_keypad_probe,
.remove = w90p910_keypad_remove,
.driver = {
.name = "nuc900-kpi",
.owner = THIS_MODULE,
},
};
module_platform_driver(w90p910_keypad_driver);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910 keypad driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nuc900-keypad");

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* XT keyboard driver for Linux
*/
/*
* 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
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#define DRIVER_DESC "XT keyboard driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define XTKBD_EMUL0 0xe0
#define XTKBD_EMUL1 0xe1
#define XTKBD_KEY 0x7f
#define XTKBD_RELEASE 0x80
static unsigned char xtkbd_keycode[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
106
};
struct xtkbd {
unsigned char keycode[256];
struct input_dev *dev;
struct serio *serio;
char phys[32];
};
static irqreturn_t xtkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct xtkbd *xtkbd = serio_get_drvdata(serio);
switch (data) {
case XTKBD_EMUL0:
case XTKBD_EMUL1:
break;
default:
if (xtkbd->keycode[data & XTKBD_KEY]) {
input_report_key(xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
input_sync(xtkbd->dev);
} else {
printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
}
}
return IRQ_HANDLED;
}
static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct xtkbd *xtkbd;
struct input_dev *input_dev;
int err = -ENOMEM;
int i;
xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!xtkbd || !input_dev)
goto fail1;
xtkbd->serio = serio;
xtkbd->dev = input_dev;
snprintf(xtkbd->phys, sizeof(xtkbd->phys), "%s/input0", serio->phys);
memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
input_dev->name = "XT Keyboard";
input_dev->phys = xtkbd->phys;
input_dev->id.bustype = BUS_XTKBD;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_dev->keycode = xtkbd->keycode;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(xtkbd_keycode);
for (i = 0; i < 255; i++)
set_bit(xtkbd->keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
serio_set_drvdata(serio, xtkbd);
err = serio_open(serio, drv);
if (err)
goto fail2;
err = input_register_device(xtkbd->dev);
if (err)
goto fail3;
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(xtkbd);
return err;
}
static void xtkbd_disconnect(struct serio *serio)
{
struct xtkbd *xtkbd = serio_get_drvdata(serio);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_unregister_device(xtkbd->dev);
kfree(xtkbd);
}
static struct serio_device_id xtkbd_serio_ids[] = {
{
.type = SERIO_XT,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, xtkbd_serio_ids);
static struct serio_driver xtkbd_drv = {
.driver = {
.name = "xtkbd",
},
.description = DRIVER_DESC,
.id_table = xtkbd_serio_ids,
.interrupt = xtkbd_interrupt,
.connect = xtkbd_connect,
.disconnect = xtkbd_disconnect,
};
module_serio_driver(xtkbd_drv);