diff options
Diffstat (limited to 'drivers/input/keyboard')
59 files changed, 13000 insertions, 3071 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 02c836e1181..f7e79b48134 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # menuconfig INPUT_KEYBOARD - bool "Keyboards" if EMBEDDED || !X86 + bool "Keyboards" default y help Say Y here, and a list of supported keyboards will be displayed. @@ -12,18 +12,6 @@ menuconfig INPUT_KEYBOARD if INPUT_KEYBOARD -config KEYBOARD_AAED2000 - tristate "AAED-2000 keyboard" - depends on MACH_AAED2000 - select INPUT_POLLDEV - default y - help - Say Y here to enable the keyboard on the Agilent AAED-2000 - development board. - - To compile this driver as a module, choose M here: the - module will be called aaed2000_kbd. - config KEYBOARD_ADP5520 tristate "Keypad Support for ADP5520 PMIC" depends on PMIC_ADP5520 @@ -35,15 +23,25 @@ config KEYBOARD_ADP5520 be called adp5520-keys. config KEYBOARD_ADP5588 - tristate "ADP5588 I2C QWERTY Keypad and IO Expander" + tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander" depends on I2C help - Say Y here if you want to use a ADP5588 attached to your + 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 @@ -69,11 +67,11 @@ config KEYBOARD_ATARI module will be called atakbd. config KEYBOARD_ATKBD - tristate "AT keyboard" if EMBEDDED || !X86 + tristate "AT keyboard" default y select SERIO select SERIO_LIBPS2 - select SERIO_I8042 if X86 + select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO select SERIO_GSCPS2 if GSC help Say Y here if you want to use a standard AT or PS/2 keyboard. Usually @@ -124,9 +122,19 @@ config KEYBOARD_ATKBD_RDI_KEYCODES right-hand column will be interpreted as the key shown in the left-hand column. -config QT2160 +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 && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Atmel AT42QT2160 Touch Sensor chip as a keyboard input. @@ -143,16 +151,17 @@ config KEYBOARD_BFIN To compile this driver as a module, choose M here: the module will be called bf54x-keys. -config KEYBOARD_CORGI - tristate "Corgi keyboard" - depends on PXA_SHARPSL - default y +config KEYBOARD_CLPS711X + tristate "CLPS711X Keypad support" + depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST) + select INPUT_MATRIXKMAP + select INPUT_POLLDEV help - Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx - series of PDAs. + Say Y here to enable the matrix keypad on the Cirrus Logic + CLPS711X CPUs. To compile this driver as a module, choose M here: the - module will be called corgikbd. + module will be called clps711x-keypad. config KEYBOARD_LKKBD tristate "DECstation/VAXstation LK201/LK401 keyboard" @@ -169,6 +178,7 @@ config KEYBOARD_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. @@ -177,7 +187,7 @@ config KEYBOARD_EP93XX config KEYBOARD_GPIO tristate "GPIO Buttons" - depends on GENERIC_GPIO + depends on GPIOLIB help This driver implements support for buttons connected to GPIO pins of various CPUs (and some other chips). @@ -190,9 +200,61 @@ config KEYBOARD_GPIO 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 + 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 GENERIC_GPIO + depends on GPIOLIB + select INPUT_MATRIXKMAP help Enable support for GPIO driven matrix keypad. @@ -262,6 +324,17 @@ config KEYBOARD_LM8323 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 @@ -271,6 +344,17 @@ config KEYBOARD_LOCOMO 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 @@ -292,6 +376,40 @@ config KEYBOARD_MAX7359 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 @@ -301,8 +419,41 @@ config KEYBOARD_NEWTON 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_NSPIRE + tristate "TI-NSPIRE built-in keyboard" + depends on ARCH_NSPIRE && OF + select INPUT_MATRIXKMAP + help + Say Y here if you want to use the built-in keypad on TI-NSPIRE. + + To compile this driver as a module, choose M here: the + module will be called nspire-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 @@ -312,7 +463,8 @@ config KEYBOARD_OPENCORES config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" - depends on PXA27x || PXA3xx + depends on PXA27x || PXA3xx || ARCH_MMP + select INPUT_MATRIXKMAP help Enable support for PXA27x/PXA3xx keypad controller. @@ -328,16 +480,38 @@ config KEYBOARD_PXA930_ROTARY To compile this driver as a module, choose M here: the module will be called pxa930_rotary. -config KEYBOARD_SPITZ - tristate "Spitz keyboard" - depends on PXA_SHARPSL - default y +config KEYBOARD_PMIC8XXX + tristate "Qualcomm PMIC8XXX keypad support" + depends on MFD_PM8XXX + select INPUT_MATRIXKMAP help - Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000, - SL-C3000 and Sl-C3100 series of PDAs. + 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 spitzkbd. + 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" @@ -350,6 +524,17 @@ config KEYBOARD_STOWAWAY To compile this driver as a module, choose M here: the module will be called stowaway. +config KEYBOARD_ST_KEYSCAN + tristate "STMicroelectronics keyscan support" + depends on ARCH_STI || COMPILE_TEST + select INPUT_MATRIXKMAP + help + Say Y here if you want to use a keypad attached to the keyscan block + on some STMicroelectronics SoC devices. + + To compile this driver as a module, choose M here: the + module will be called st-keyscan. + config KEYBOARD_SUNKBD tristate "Sun Type 4 and Type 5 keyboard" select SERIO @@ -363,7 +548,7 @@ config KEYBOARD_SUNKBD config KEYBOARD_SH_KEYSC tristate "SuperH KEYSC keypad support" - depends on SUPERH + depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST help Say Y here if you want to use a keypad attached to the KEYSC block on SuperH processors such as sh7722 and sh7343. @@ -371,6 +556,17 @@ config KEYBOARD_SH_KEYSC 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 @@ -383,43 +579,56 @@ config KEYBOARD_DAVINCI config KEYBOARD_OMAP tristate "TI OMAP keypad support" - depends on (ARCH_OMAP1 || ARCH_OMAP2) + 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_TWL4030 - tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" - depends on TWL4030_CORE +config KEYBOARD_OMAP4 + tristate "TI OMAP4+ keypad support" + depends on OF || ARCH_OMAP2PLUS + 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. + 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 twl4030_keypad. + module will be called omap4-keypad. -config KEYBOARD_TOSA - tristate "Tosa keyboard" - depends on MACH_TOSA - default y +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 to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) + 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 tosakbd. + module will be called tc3589x-keypad. -config KEYBOARD_TOSA_USE_EXT_KEYCODES - bool "Tosa keyboard: use extended keycodes" - depends on KEYBOARD_TOSA +config KEYBOARD_TWL4030 + tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" + depends on TWL4030_CORE + select INPUT_MATRIXKMAP help - Say Y here to enable the tosa keyboard driver to generate extended - (>= 127) keycodes. Be aware, that they can't be correctly interpreted - by either console keyboard driver or by Kdrive keybd driver. + 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. - Say Y only if you know, what you are doing! + To compile this driver as a module, choose M here: the + module will be called twl4030_keypad. config KEYBOARD_XTKBD tristate "XT keyboard" @@ -436,6 +645,7 @@ config KEYBOARD_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. @@ -443,4 +653,16 @@ config KEYBOARD_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 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 78654ef6520..7504ae19049 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -4,38 +4,57 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o 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_CORGI) += corgikbd.o +obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.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_NSPIRE) += nspire-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_SPITZ) += spitzkbd.o +obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o +obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o +obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o -obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o +obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c deleted file mode 100644 index 18222a689a0..00000000000 --- a/drivers/input/keyboard/aaed2000_kbd.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Keyboard driver for the AAED-2000 dev board - * - * Copyright (c) 2006 Nicolas Bellido Y Ortega - * - * 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/delay.h> -#include <linux/platform_device.h> -#include <linux/init.h> -#include <linux/input-polldev.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/slab.h> - -#include <mach/hardware.h> -#include <mach/aaed2000.h> - -#define KB_ROWS 12 -#define KB_COLS 8 -#define KB_ROWMASK(r) (1 << (r)) -#define SCANCODE(r,c) (((c) * KB_ROWS) + (r)) -#define NR_SCANCODES (KB_COLS * KB_ROWS) - -#define SCAN_INTERVAL (50) /* ms */ -#define KB_ACTIVATE_DELAY (20) /* us */ - -static unsigned char aaedkbd_keycode[NR_SCANCODES] = { - KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0, - KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0, - KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0, - KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, - KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK, - KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB, - KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE, - 0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL -}; - -struct aaedkbd { - unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)]; - struct input_polled_dev *poll_dev; - int kbdscan_state[KB_COLS]; - int kbdscan_count[KB_COLS]; -}; - -#define KBDSCAN_STABLE_COUNT 2 - -static void aaedkbd_report_col(struct aaedkbd *aaedkbd, - unsigned int col, unsigned int rowd) -{ - unsigned int scancode, pressed; - unsigned int row; - - for (row = 0; row < KB_ROWS; row++) { - scancode = SCANCODE(row, col); - pressed = rowd & KB_ROWMASK(row); - - input_report_key(aaedkbd->poll_dev->input, - aaedkbd->keycode[scancode], pressed); - } -} - -/* Scan the hardware keyboard and push any changes up through the input layer */ -static void aaedkbd_poll(struct input_polled_dev *dev) -{ - struct aaedkbd *aaedkbd = dev->private; - unsigned int col, rowd; - - col = 0; - do { - AAEC_GPIO_KSCAN = col + 8; - udelay(KB_ACTIVATE_DELAY); - rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN; - - if (rowd != aaedkbd->kbdscan_state[col]) { - aaedkbd->kbdscan_count[col] = 0; - aaedkbd->kbdscan_state[col] = rowd; - } else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) { - aaedkbd_report_col(aaedkbd, col, rowd); - col++; - } - } while (col < KB_COLS); - - AAEC_GPIO_KSCAN = 0x07; - input_sync(dev->input); -} - -static int __devinit aaedkbd_probe(struct platform_device *pdev) -{ - struct aaedkbd *aaedkbd; - struct input_polled_dev *poll_dev; - struct input_dev *input_dev; - int i; - int error; - - aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL); - poll_dev = input_allocate_polled_device(); - if (!aaedkbd || !poll_dev) { - error = -ENOMEM; - goto fail; - } - - platform_set_drvdata(pdev, aaedkbd); - - aaedkbd->poll_dev = poll_dev; - memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode)); - - poll_dev->private = aaedkbd; - poll_dev->poll = aaedkbd_poll; - poll_dev->poll_interval = SCAN_INTERVAL; - - input_dev = poll_dev->input; - input_dev->name = "AAED-2000 Keyboard"; - input_dev->phys = "aaedkbd/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &pdev->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_dev->keycode = aaedkbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); - input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode); - - for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++) - set_bit(aaedkbd->keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); - - error = input_register_polled_device(aaedkbd->poll_dev); - if (error) - goto fail; - - return 0; - - fail: kfree(aaedkbd); - input_free_polled_device(poll_dev); - return error; -} - -static int __devexit aaedkbd_remove(struct platform_device *pdev) -{ - struct aaedkbd *aaedkbd = platform_get_drvdata(pdev); - - input_unregister_polled_device(aaedkbd->poll_dev); - input_free_polled_device(aaedkbd->poll_dev); - kfree(aaedkbd); - - return 0; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:aaed2000-keyboard"); - -static struct platform_driver aaedkbd_driver = { - .probe = aaedkbd_probe, - .remove = __devexit_p(aaedkbd_remove), - .driver = { - .name = "aaed2000-keyboard", - .owner = THIS_MODULE, - }, -}; - -static int __init aaedkbd_init(void) -{ - return platform_driver_register(&aaedkbd_driver); -} - -static void __exit aaedkbd_exit(void) -{ - platform_driver_unregister(&aaedkbd_driver); -} - -module_init(aaedkbd_init); -module_exit(aaedkbd_exit); - -MODULE_AUTHOR("Nicolas Bellido Y Ortega"); -MODULE_DESCRIPTION("AAED-2000 Keyboard Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/adp5520-keys.c b/drivers/input/keyboard/adp5520-keys.c index a7ba27fb410..7f4a8b58efc 100644 --- a/drivers/input/keyboard/adp5520-keys.c +++ b/drivers/input/keyboard/adp5520-keys.c @@ -8,10 +8,11 @@ #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> +#include <linux/device.h> struct adp5520_keys { struct input_dev *input; @@ -68,9 +69,9 @@ static int adp5520_keys_notifier(struct notifier_block *nb, return 0; } -static int __devinit adp5520_keys_probe(struct platform_device *pdev) +static int adp5520_keys_probe(struct platform_device *pdev) { - struct adp5520_keys_platform_data *pdata = pdev->dev.platform_data; + struct adp5520_keys_platform_data *pdata = dev_get_platdata(&pdev->dev); struct input_dev *input; struct adp5520_keys *dev; int ret, i; @@ -81,7 +82,7 @@ static int __devinit adp5520_keys_probe(struct platform_device *pdev) return -EINVAL; } - if (pdata == NULL) { + if (!pdata) { dev_err(&pdev->dev, "missing platform data\n"); return -EINVAL; } @@ -89,17 +90,15 @@ static int __devinit adp5520_keys_probe(struct platform_device *pdev) if (!(pdata->rows_en_mask && pdata->cols_en_mask)) return -EINVAL; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { dev_err(&pdev->dev, "failed to alloc memory\n"); return -ENOMEM; } - input = input_allocate_device(); - if (!input) { - ret = -ENOMEM; - goto err; - } + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; dev->master = pdev->dev.parent; dev->input = input; @@ -135,7 +134,7 @@ static int __devinit adp5520_keys_probe(struct platform_device *pdev) ret = input_register_device(input); if (ret) { dev_err(&pdev->dev, "unable to register input device\n"); - goto err; + return ret; } en_mask = pdata->rows_en_mask | pdata->cols_en_mask; @@ -157,8 +156,7 @@ static int __devinit adp5520_keys_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to write\n"); - ret = -EIO; - goto err1; + return -EIO; } dev->notifier.notifier_call = adp5520_keys_notifier; @@ -166,30 +164,20 @@ static int __devinit adp5520_keys_probe(struct platform_device *pdev) ADP5520_KP_IEN | ADP5520_KR_IEN); if (ret) { dev_err(&pdev->dev, "failed to register notifier\n"); - goto err1; + return ret; } 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 __devexit adp5520_keys_remove(struct platform_device *pdev) +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; } @@ -199,20 +187,9 @@ static struct platform_driver adp5520_keys_driver = { .owner = THIS_MODULE, }, .probe = adp5520_keys_probe, - .remove = __devexit_p(adp5520_keys_remove), + .remove = adp5520_keys_remove, }; - -static int __init adp5520_keys_init(void) -{ - return platform_driver_register(&adp5520_keys_driver); -} -module_init(adp5520_keys_init); - -static void __exit adp5520_keys_exit(void) -{ - platform_driver_unregister(&adp5520_keys_driver); -} -module_exit(adp5520_keys_exit); +module_platform_driver(adp5520_keys_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Keys ADP5520 Driver"); diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 1edb596d927..5ef7fcf0e25 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -1,15 +1,14 @@ /* * File: drivers/input/keyboard/adp5588_keys.c - * Description: keypad driver for ADP5588 I2C QWERTY Keypad and IO Expander + * Description: keypad driver for ADP5588 and ADP5587 + * I2C QWERTY Keypad and IO Expander * Bugs: Enter bugs at http://blackfin.uclinux.org/ * - * Copyright (C) 2008-2009 Analog Devices Inc. + * Copyright (C) 2008-2010 Analog Devices Inc. * Licensed under the GPL-2 or later. */ #include <linux/module.h> -#include <linux/version.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/workqueue.h> @@ -18,32 +17,11 @@ #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> - /* Configuration Register1 */ -#define AUTO_INC (1 << 7) -#define GPIEM_CFG (1 << 6) -#define OVR_FLOW_M (1 << 5) -#define INT_CFG (1 << 4) -#define OVR_FLOW_IEN (1 << 3) -#define K_LCK_IM (1 << 2) -#define GPI_IEN (1 << 1) -#define KE_IEN (1 << 0) - -/* Interrupt Status Register */ -#define CMP2_INT (1 << 5) -#define CMP1_INT (1 << 4) -#define OVR_FLOW_INT (1 << 3) -#define K_LCK_INT (1 << 2) -#define GPI_INT (1 << 1) -#define KE_INT (1 << 0) - -/* Key Lock and Event Counter Register */ -#define K_LCK_EN (1 << 6) -#define LCK21 0x30 -#define KEC 0xF - /* Key Event Register xy */ #define KEY_EV_PRESSED (1 << 7) #define KEY_EV_MASK (0x7F) @@ -65,6 +43,16 @@ struct adp5588_kpad { 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) @@ -82,27 +70,243 @@ 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]); + int val; + + mutex_lock(&kpad->gpio_lock); + + if (kpad->dir[bank] & bit) + val = kpad->dat_out[bank]; + else + val = adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank); + + mutex_unlock(&kpad->gpio_lock); + + return !!(val & 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_get_platdata(dev); + 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_get_platdata(dev); + 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 i, key, status, ev_cnt; + int status, ev_cnt; status = adp5588_read(client, INT_STAT); - if (status & OVR_FLOW_INT) /* Unlikely and should never happen */ + if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ dev_err(&client->dev, "Event Overflow Error\n"); - if (status & KE_INT) { - ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC; + if (status & ADP5588_KE_INT) { + ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & ADP5588_KEC; if (ev_cnt) { - for (i = 0; i < ev_cnt; i++) { - key = adp5588_read(client, Key_EVENTA + i); - input_report_key(kpad->input, - kpad->keycode[(key & KEY_EV_MASK) - 1], - key & KEY_EV_PRESSED); - } + adp5588_report_events(kpad, ev_cnt); input_sync(kpad->input); } } @@ -124,10 +328,13 @@ static irqreturn_t adp5588_irq(int irq, void *handle) return IRQ_HANDLED; } -static int __devinit adp5588_setup(struct i2c_client *client) +static int adp5588_setup(struct i2c_client *client) { - struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + const struct adp5588_kpad_platform_data *pdata = + dev_get_platdata(&client->dev); + 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); @@ -136,17 +343,46 @@ static int __devinit adp5588_setup(struct i2c_client *client) 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, K_LCK_EN); + 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); - ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | - OVR_FLOW_INT | K_LCK_INT | - GPI_INT | KE_INT); /* Status is W1C */ + 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, INT_CFG | OVR_FLOW_IEN | KE_IEN); + 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"); @@ -156,11 +392,50 @@ static int __devinit adp5588_setup(struct i2c_client *client) return 0; } -static int __devinit adp5588_probe(struct i2c_client *client, - const struct i2c_device_id *id) +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; - struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + const struct adp5588_kpad_platform_data *pdata = + dev_get_platdata(&client->dev); struct input_dev *input; unsigned int revid; int ret, i; @@ -187,6 +462,37 @@ static int __devinit adp5588_probe(struct i2c_client *client, 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; @@ -231,6 +537,9 @@ static int __devinit adp5588_probe(struct i2c_client *client, 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); @@ -238,9 +547,15 @@ static int __devinit adp5588_probe(struct i2c_client *client, __set_bit(EV_REP, input->evbit); for (i = 0; i < input->keycodemax; i++) - __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + if (kpad->keycode[i] <= KEY_MAX) + __set_bit(kpad->keycode[i], 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"); @@ -248,7 +563,7 @@ static int __devinit adp5588_probe(struct i2c_client *client, } error = request_irq(client->irq, adp5588_irq, - IRQF_TRIGGER_FALLING | IRQF_DISABLED, + IRQF_TRIGGER_FALLING, client->dev.driver->name, kpad); if (error) { dev_err(&client->dev, "irq %d busy?\n", client->irq); @@ -259,6 +574,13 @@ static int __devinit adp5588_probe(struct i2c_client *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); @@ -277,7 +599,7 @@ static int __devinit adp5588_probe(struct i2c_client *client, return error; } -static int __devexit adp5588_remove(struct i2c_client *client) +static int adp5588_remove(struct i2c_client *client) { struct adp5588_kpad *kpad = i2c_get_clientdata(client); @@ -285,7 +607,7 @@ static int __devexit adp5588_remove(struct i2c_client *client) free_irq(client->irq, kpad); cancel_delayed_work_sync(&kpad->work); input_unregister_device(kpad->input); - i2c_set_clientdata(client, NULL); + adp5588_gpio_remove(kpad); kfree(kpad); return 0; @@ -326,7 +648,8 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = { #endif static const struct i2c_device_id adp5588_id[] = { - { KBUILD_MODNAME, 0 }, + { "adp5588-keys", 0 }, + { "adp5587-keys", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, adp5588_id); @@ -339,23 +662,12 @@ static struct i2c_driver adp5588_driver = { #endif }, .probe = adp5588_probe, - .remove = __devexit_p(adp5588_remove), + .remove = adp5588_remove, .id_table = adp5588_id, }; -static int __init adp5588_init(void) -{ - return i2c_add_driver(&adp5588_driver); -} -module_init(adp5588_init); - -static void __exit adp5588_exit(void) -{ - i2c_del_driver(&adp5588_driver); -} -module_exit(adp5588_exit); +module_i2c_driver(adp5588_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); -MODULE_DESCRIPTION("ADP5588 Keypad driver"); -MODULE_ALIAS("platform:adp5588-keys"); +MODULE_DESCRIPTION("ADP5588/87 Keypad driver"); diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c new file mode 100644 index 00000000000..6329549bf6a --- /dev/null +++ b/drivers/input/keyboard/adp5589-keys.c @@ -0,0 +1,1114 @@ +/* + * Description: keypad driver for ADP5589, ADP5585 + * I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2010-2011 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#include <linux/module.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/input/adp5589.h> + +/* ADP5589/ADP5585 Common Registers */ +#define ADP5589_5_ID 0x00 +#define ADP5589_5_INT_STATUS 0x01 +#define ADP5589_5_STATUS 0x02 +#define ADP5589_5_FIFO_1 0x03 +#define ADP5589_5_FIFO_2 0x04 +#define ADP5589_5_FIFO_3 0x05 +#define ADP5589_5_FIFO_4 0x06 +#define ADP5589_5_FIFO_5 0x07 +#define ADP5589_5_FIFO_6 0x08 +#define ADP5589_5_FIFO_7 0x09 +#define ADP5589_5_FIFO_8 0x0A +#define ADP5589_5_FIFO_9 0x0B +#define ADP5589_5_FIFO_10 0x0C +#define ADP5589_5_FIFO_11 0x0D +#define ADP5589_5_FIFO_12 0x0E +#define ADP5589_5_FIFO_13 0x0F +#define ADP5589_5_FIFO_14 0x10 +#define ADP5589_5_FIFO_15 0x11 +#define ADP5589_5_FIFO_16 0x12 +#define ADP5589_5_GPI_INT_STAT_A 0x13 +#define ADP5589_5_GPI_INT_STAT_B 0x14 + +/* ADP5589 Registers */ +#define ADP5589_GPI_INT_STAT_C 0x15 +#define ADP5589_GPI_STATUS_A 0x16 +#define ADP5589_GPI_STATUS_B 0x17 +#define ADP5589_GPI_STATUS_C 0x18 +#define ADP5589_RPULL_CONFIG_A 0x19 +#define ADP5589_RPULL_CONFIG_B 0x1A +#define ADP5589_RPULL_CONFIG_C 0x1B +#define ADP5589_RPULL_CONFIG_D 0x1C +#define ADP5589_RPULL_CONFIG_E 0x1D +#define ADP5589_GPI_INT_LEVEL_A 0x1E +#define ADP5589_GPI_INT_LEVEL_B 0x1F +#define ADP5589_GPI_INT_LEVEL_C 0x20 +#define ADP5589_GPI_EVENT_EN_A 0x21 +#define ADP5589_GPI_EVENT_EN_B 0x22 +#define ADP5589_GPI_EVENT_EN_C 0x23 +#define ADP5589_GPI_INTERRUPT_EN_A 0x24 +#define ADP5589_GPI_INTERRUPT_EN_B 0x25 +#define ADP5589_GPI_INTERRUPT_EN_C 0x26 +#define ADP5589_DEBOUNCE_DIS_A 0x27 +#define ADP5589_DEBOUNCE_DIS_B 0x28 +#define ADP5589_DEBOUNCE_DIS_C 0x29 +#define ADP5589_GPO_DATA_OUT_A 0x2A +#define ADP5589_GPO_DATA_OUT_B 0x2B +#define ADP5589_GPO_DATA_OUT_C 0x2C +#define ADP5589_GPO_OUT_MODE_A 0x2D +#define ADP5589_GPO_OUT_MODE_B 0x2E +#define ADP5589_GPO_OUT_MODE_C 0x2F +#define ADP5589_GPIO_DIRECTION_A 0x30 +#define ADP5589_GPIO_DIRECTION_B 0x31 +#define ADP5589_GPIO_DIRECTION_C 0x32 +#define ADP5589_UNLOCK1 0x33 +#define ADP5589_UNLOCK2 0x34 +#define ADP5589_EXT_LOCK_EVENT 0x35 +#define ADP5589_UNLOCK_TIMERS 0x36 +#define ADP5589_LOCK_CFG 0x37 +#define ADP5589_RESET1_EVENT_A 0x38 +#define ADP5589_RESET1_EVENT_B 0x39 +#define ADP5589_RESET1_EVENT_C 0x3A +#define ADP5589_RESET2_EVENT_A 0x3B +#define ADP5589_RESET2_EVENT_B 0x3C +#define ADP5589_RESET_CFG 0x3D +#define ADP5589_PWM_OFFT_LOW 0x3E +#define ADP5589_PWM_OFFT_HIGH 0x3F +#define ADP5589_PWM_ONT_LOW 0x40 +#define ADP5589_PWM_ONT_HIGH 0x41 +#define ADP5589_PWM_CFG 0x42 +#define ADP5589_CLOCK_DIV_CFG 0x43 +#define ADP5589_LOGIC_1_CFG 0x44 +#define ADP5589_LOGIC_2_CFG 0x45 +#define ADP5589_LOGIC_FF_CFG 0x46 +#define ADP5589_LOGIC_INT_EVENT_EN 0x47 +#define ADP5589_POLL_PTIME_CFG 0x48 +#define ADP5589_PIN_CONFIG_A 0x49 +#define ADP5589_PIN_CONFIG_B 0x4A +#define ADP5589_PIN_CONFIG_C 0x4B +#define ADP5589_PIN_CONFIG_D 0x4C +#define ADP5589_GENERAL_CFG 0x4D +#define ADP5589_INT_EN 0x4E + +/* ADP5585 Registers */ +#define ADP5585_GPI_STATUS_A 0x15 +#define ADP5585_GPI_STATUS_B 0x16 +#define ADP5585_RPULL_CONFIG_A 0x17 +#define ADP5585_RPULL_CONFIG_B 0x18 +#define ADP5585_RPULL_CONFIG_C 0x19 +#define ADP5585_RPULL_CONFIG_D 0x1A +#define ADP5585_GPI_INT_LEVEL_A 0x1B +#define ADP5585_GPI_INT_LEVEL_B 0x1C +#define ADP5585_GPI_EVENT_EN_A 0x1D +#define ADP5585_GPI_EVENT_EN_B 0x1E +#define ADP5585_GPI_INTERRUPT_EN_A 0x1F +#define ADP5585_GPI_INTERRUPT_EN_B 0x20 +#define ADP5585_DEBOUNCE_DIS_A 0x21 +#define ADP5585_DEBOUNCE_DIS_B 0x22 +#define ADP5585_GPO_DATA_OUT_A 0x23 +#define ADP5585_GPO_DATA_OUT_B 0x24 +#define ADP5585_GPO_OUT_MODE_A 0x25 +#define ADP5585_GPO_OUT_MODE_B 0x26 +#define ADP5585_GPIO_DIRECTION_A 0x27 +#define ADP5585_GPIO_DIRECTION_B 0x28 +#define ADP5585_RESET1_EVENT_A 0x29 +#define ADP5585_RESET1_EVENT_B 0x2A +#define ADP5585_RESET1_EVENT_C 0x2B +#define ADP5585_RESET2_EVENT_A 0x2C +#define ADP5585_RESET2_EVENT_B 0x2D +#define ADP5585_RESET_CFG 0x2E +#define ADP5585_PWM_OFFT_LOW 0x2F +#define ADP5585_PWM_OFFT_HIGH 0x30 +#define ADP5585_PWM_ONT_LOW 0x31 +#define ADP5585_PWM_ONT_HIGH 0x32 +#define ADP5585_PWM_CFG 0x33 +#define ADP5585_LOGIC_CFG 0x34 +#define ADP5585_LOGIC_FF_CFG 0x35 +#define ADP5585_LOGIC_INT_EVENT_EN 0x36 +#define ADP5585_POLL_PTIME_CFG 0x37 +#define ADP5585_PIN_CONFIG_A 0x38 +#define ADP5585_PIN_CONFIG_B 0x39 +#define ADP5585_PIN_CONFIG_D 0x3A +#define ADP5585_GENERAL_CFG 0x3B +#define ADP5585_INT_EN 0x3C + +/* ID Register */ +#define ADP5589_5_DEVICE_ID_MASK 0xF +#define ADP5589_5_MAN_ID_MASK 0xF +#define ADP5589_5_MAN_ID_SHIFT 4 +#define ADP5589_5_MAN_ID 0x02 + +/* GENERAL_CFG Register */ +#define OSC_EN (1 << 7) +#define CORE_CLK(x) (((x) & 0x3) << 5) +#define LCK_TRK_LOGIC (1 << 4) /* ADP5589 only */ +#define LCK_TRK_GPI (1 << 3) /* ADP5589 only */ +#define INT_CFG (1 << 1) +#define RST_CFG (1 << 0) + +/* INT_EN Register */ +#define LOGIC2_IEN (1 << 5) /* ADP5589 only */ +#define LOGIC1_IEN (1 << 4) +#define LOCK_IEN (1 << 3) /* ADP5589 only */ +#define OVRFLOW_IEN (1 << 2) +#define GPI_IEN (1 << 1) +#define EVENT_IEN (1 << 0) + +/* Interrupt Status Register */ +#define LOGIC2_INT (1 << 5) /* ADP5589 only */ +#define LOGIC1_INT (1 << 4) +#define LOCK_INT (1 << 3) /* ADP5589 only */ +#define OVRFLOW_INT (1 << 2) +#define GPI_INT (1 << 1) +#define EVENT_INT (1 << 0) + +/* STATUS Register */ +#define LOGIC2_STAT (1 << 7) /* ADP5589 only */ +#define LOGIC1_STAT (1 << 6) +#define LOCK_STAT (1 << 5) /* ADP5589 only */ +#define KEC 0xF + +/* PIN_CONFIG_D Register */ +#define C4_EXTEND_CFG (1 << 6) /* RESET2 */ +#define R4_EXTEND_CFG (1 << 5) /* RESET1 */ + +/* LOCK_CFG */ +#define LOCK_EN (1 << 0) + +#define PTIME_MASK 0x3 +#define LTIME_MASK 0x3 /* ADP5589 only */ + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KEYP_MAX_EVENT 16 +#define ADP5589_MAXGPIO 19 +#define ADP5585_MAXGPIO 11 /* 10 on the ADP5585-01, 11 on ADP5585-02 */ + +enum { + ADP5589, + ADP5585_01, + ADP5585_02 +}; + +struct adp_constants { + u8 maxgpio; + u8 keymapsize; + u8 gpi_pin_row_base; + u8 gpi_pin_row_end; + u8 gpi_pin_col_base; + u8 gpi_pin_base; + u8 gpi_pin_end; + u8 gpimapsize_max; + u8 max_row_num; + u8 max_col_num; + u8 row_mask; + u8 col_mask; + u8 col_shift; + u8 c4_extend_cfg; + u8 (*bank) (u8 offset); + u8 (*bit) (u8 offset); + u8 (*reg) (u8 reg); +}; + +struct adp5589_kpad { + struct i2c_client *client; + struct input_dev *input; + const struct adp_constants *var; + unsigned short keycode[ADP5589_KEYMAPSIZE]; + const struct adp5589_gpi_map *gpimap; + unsigned short gpimapsize; + unsigned extend_cfg; + bool is_adp5585; + bool adp5585_support_row5; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[ADP5589_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 +}; + +/* + * ADP5589 / ADP5585 derivative / variant handling + */ + + +/* ADP5589 */ + +static unsigned char adp5589_bank(unsigned char offset) +{ + return offset >> 3; +} + +static unsigned char adp5589_bit(unsigned char offset) +{ + return 1u << (offset & 0x7); +} + +static unsigned char adp5589_reg(unsigned char reg) +{ + return reg; +} + +static const struct adp_constants const_adp5589 = { + .maxgpio = ADP5589_MAXGPIO, + .keymapsize = ADP5589_KEYMAPSIZE, + .gpi_pin_row_base = ADP5589_GPI_PIN_ROW_BASE, + .gpi_pin_row_end = ADP5589_GPI_PIN_ROW_END, + .gpi_pin_col_base = ADP5589_GPI_PIN_COL_BASE, + .gpi_pin_base = ADP5589_GPI_PIN_BASE, + .gpi_pin_end = ADP5589_GPI_PIN_END, + .gpimapsize_max = ADP5589_GPIMAPSIZE_MAX, + .c4_extend_cfg = 12, + .max_row_num = ADP5589_MAX_ROW_NUM, + .max_col_num = ADP5589_MAX_COL_NUM, + .row_mask = ADP5589_ROW_MASK, + .col_mask = ADP5589_COL_MASK, + .col_shift = ADP5589_COL_SHIFT, + .bank = adp5589_bank, + .bit = adp5589_bit, + .reg = adp5589_reg, +}; + +/* ADP5585 */ + +static unsigned char adp5585_bank(unsigned char offset) +{ + return offset > ADP5585_MAX_ROW_NUM; +} + +static unsigned char adp5585_bit(unsigned char offset) +{ + return (offset > ADP5585_MAX_ROW_NUM) ? + 1u << (offset - ADP5585_COL_SHIFT) : 1u << offset; +} + +static const unsigned char adp5585_reg_lut[] = { + [ADP5589_GPI_STATUS_A] = ADP5585_GPI_STATUS_A, + [ADP5589_GPI_STATUS_B] = ADP5585_GPI_STATUS_B, + [ADP5589_RPULL_CONFIG_A] = ADP5585_RPULL_CONFIG_A, + [ADP5589_RPULL_CONFIG_B] = ADP5585_RPULL_CONFIG_B, + [ADP5589_RPULL_CONFIG_C] = ADP5585_RPULL_CONFIG_C, + [ADP5589_RPULL_CONFIG_D] = ADP5585_RPULL_CONFIG_D, + [ADP5589_GPI_INT_LEVEL_A] = ADP5585_GPI_INT_LEVEL_A, + [ADP5589_GPI_INT_LEVEL_B] = ADP5585_GPI_INT_LEVEL_B, + [ADP5589_GPI_EVENT_EN_A] = ADP5585_GPI_EVENT_EN_A, + [ADP5589_GPI_EVENT_EN_B] = ADP5585_GPI_EVENT_EN_B, + [ADP5589_GPI_INTERRUPT_EN_A] = ADP5585_GPI_INTERRUPT_EN_A, + [ADP5589_GPI_INTERRUPT_EN_B] = ADP5585_GPI_INTERRUPT_EN_B, + [ADP5589_DEBOUNCE_DIS_A] = ADP5585_DEBOUNCE_DIS_A, + [ADP5589_DEBOUNCE_DIS_B] = ADP5585_DEBOUNCE_DIS_B, + [ADP5589_GPO_DATA_OUT_A] = ADP5585_GPO_DATA_OUT_A, + [ADP5589_GPO_DATA_OUT_B] = ADP5585_GPO_DATA_OUT_B, + [ADP5589_GPO_OUT_MODE_A] = ADP5585_GPO_OUT_MODE_A, + [ADP5589_GPO_OUT_MODE_B] = ADP5585_GPO_OUT_MODE_B, + [ADP5589_GPIO_DIRECTION_A] = ADP5585_GPIO_DIRECTION_A, + [ADP5589_GPIO_DIRECTION_B] = ADP5585_GPIO_DIRECTION_B, + [ADP5589_RESET1_EVENT_A] = ADP5585_RESET1_EVENT_A, + [ADP5589_RESET1_EVENT_B] = ADP5585_RESET1_EVENT_B, + [ADP5589_RESET1_EVENT_C] = ADP5585_RESET1_EVENT_C, + [ADP5589_RESET2_EVENT_A] = ADP5585_RESET2_EVENT_A, + [ADP5589_RESET2_EVENT_B] = ADP5585_RESET2_EVENT_B, + [ADP5589_RESET_CFG] = ADP5585_RESET_CFG, + [ADP5589_PWM_OFFT_LOW] = ADP5585_PWM_OFFT_LOW, + [ADP5589_PWM_OFFT_HIGH] = ADP5585_PWM_OFFT_HIGH, + [ADP5589_PWM_ONT_LOW] = ADP5585_PWM_ONT_LOW, + [ADP5589_PWM_ONT_HIGH] = ADP5585_PWM_ONT_HIGH, + [ADP5589_PWM_CFG] = ADP5585_PWM_CFG, + [ADP5589_LOGIC_1_CFG] = ADP5585_LOGIC_CFG, + [ADP5589_LOGIC_FF_CFG] = ADP5585_LOGIC_FF_CFG, + [ADP5589_LOGIC_INT_EVENT_EN] = ADP5585_LOGIC_INT_EVENT_EN, + [ADP5589_POLL_PTIME_CFG] = ADP5585_POLL_PTIME_CFG, + [ADP5589_PIN_CONFIG_A] = ADP5585_PIN_CONFIG_A, + [ADP5589_PIN_CONFIG_B] = ADP5585_PIN_CONFIG_B, + [ADP5589_PIN_CONFIG_D] = ADP5585_PIN_CONFIG_D, + [ADP5589_GENERAL_CFG] = ADP5585_GENERAL_CFG, + [ADP5589_INT_EN] = ADP5585_INT_EN, +}; + +static unsigned char adp5585_reg(unsigned char reg) +{ + return adp5585_reg_lut[reg]; +} + +static const struct adp_constants const_adp5585 = { + .maxgpio = ADP5585_MAXGPIO, + .keymapsize = ADP5585_KEYMAPSIZE, + .gpi_pin_row_base = ADP5585_GPI_PIN_ROW_BASE, + .gpi_pin_row_end = ADP5585_GPI_PIN_ROW_END, + .gpi_pin_col_base = ADP5585_GPI_PIN_COL_BASE, + .gpi_pin_base = ADP5585_GPI_PIN_BASE, + .gpi_pin_end = ADP5585_GPI_PIN_END, + .gpimapsize_max = ADP5585_GPIMAPSIZE_MAX, + .c4_extend_cfg = 10, + .max_row_num = ADP5585_MAX_ROW_NUM, + .max_col_num = ADP5585_MAX_COL_NUM, + .row_mask = ADP5585_ROW_MASK, + .col_mask = ADP5585_COL_MASK, + .col_shift = ADP5585_COL_SHIFT, + .bank = adp5585_bank, + .bit = adp5585_bit, + .reg = adp5585_reg, +}; + +static int adp5589_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 adp5589_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +#ifdef CONFIG_GPIOLIB +static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + + return !!(adp5589_read(kpad->client, + kpad->var->reg(ADP5589_GPI_STATUS_A) + bank) & + bit); +} + +static void adp5589_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + + bank, kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5589_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); + unsigned int bit = kpad->var->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 = adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + + bank, kpad->dat_out[bank]); + ret |= adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5589_build_gpiomap(struct adp5589_kpad *kpad, + const struct adp5589_kpad_platform_data *pdata) +{ + bool pin_used[ADP5589_MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, false, sizeof(pin_used)); + + for (i = 0; i < kpad->var->maxgpio; i++) + if (pdata->keypad_en_mask & (1 << i)) + pin_used[i] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - kpad->var->gpi_pin_base] = true; + + if (kpad->extend_cfg & R4_EXTEND_CFG) + pin_used[4] = true; + + if (kpad->extend_cfg & C4_EXTEND_CFG) + pin_used[kpad->var->c4_extend_cfg] = true; + + if (!kpad->adp5585_support_row5) + pin_used[5] = true; + + for (i = 0; i < kpad->var->maxgpio; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(dev); + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5589_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 = adp5589_gpio_direction_input; + kpad->gc.direction_output = adp5589_gpio_direction_output; + kpad->gc.get = adp5589_gpio_get_value; + kpad->gc.set = adp5589_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; + + 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 <= kpad->var->bank(kpad->var->maxgpio); i++) { + kpad->dat_out[i] = adp5589_read(kpad->client, kpad->var->reg( + ADP5589_GPO_DATA_OUT_A) + i); + kpad->dir[i] = adp5589_read(kpad->client, kpad->var->reg( + ADP5589_GPIO_DIRECTION_A) + 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 adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(dev); + const struct adp5589_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 adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + return 0; +} + +static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ +} +#endif + +static void adp5589_report_switches(struct adp5589_kpad *kpad, + int key, int key_val) +{ + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + if (key_val == kpad->gpimap[i].pin) { + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } +} + +static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt) +{ + int i; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5589_read(kpad->client, ADP5589_5_FIFO_1 + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= kpad->var->gpi_pin_base && + key_val <= kpad->var->gpi_pin_end) { + adp5589_report_switches(kpad, key, key_val); + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + +static irqreturn_t adp5589_irq(int irq, void *handle) +{ + struct adp5589_kpad *kpad = handle; + struct i2c_client *client = kpad->client; + int status, ev_cnt; + + status = adp5589_read(client, ADP5589_5_INT_STATUS); + + if (status & OVRFLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & EVENT_INT) { + ev_cnt = adp5589_read(client, ADP5589_5_STATUS) & KEC; + if (ev_cnt) { + adp5589_report_events(kpad, ev_cnt); + input_sync(kpad->input); + } + } + + adp5589_write(client, ADP5589_5_INT_STATUS, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key) +{ + int i; + + for (i = 0; i < kpad->var->keymapsize; i++) + if (key == kpad->keycode[i]) + return (i + 1) | KEY_EV_PRESSED; + + dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n"); + + return -EINVAL; +} + +static int adp5589_setup(struct adp5589_kpad *kpad) +{ + struct i2c_client *client = kpad->client; + const struct adp5589_kpad_platform_data *pdata = + dev_get_platdata(&client->dev); + u8 (*reg) (u8) = kpad->var->reg; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; + unsigned char pull_mask = 0; + int i, ret; + + ret = adp5589_write(client, reg(ADP5589_PIN_CONFIG_A), + pdata->keypad_en_mask & kpad->var->row_mask); + ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_B), + (pdata->keypad_en_mask >> kpad->var->col_shift) & + kpad->var->col_mask); + + if (!kpad->is_adp5585) + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C, + (pdata->keypad_en_mask >> 16) & 0xFF); + + if (!kpad->is_adp5585 && pdata->en_keylock) { + ret |= adp5589_write(client, ADP5589_UNLOCK1, + pdata->unlock_key1); + ret |= adp5589_write(client, ADP5589_UNLOCK2, + pdata->unlock_key2); + ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS, + pdata->unlock_timer & LTIME_MASK); + ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5589_read(client, ADP5589_5_FIFO_1 + i); + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= kpad->var->gpi_pin_row_end) { + evt_mode1 |= (1 << (pin - kpad->var->gpi_pin_row_base)); + } else { + evt_mode2 |= + ((1 << (pin - kpad->var->gpi_pin_col_base)) & 0xFF); + if (!kpad->is_adp5585) + evt_mode3 |= ((1 << (pin - + kpad->var->gpi_pin_col_base)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_A), + evt_mode1); + ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_B), + evt_mode2); + if (!kpad->is_adp5585) + ret |= adp5589_write(client, + reg(ADP5589_GPI_EVENT_EN_C), + evt_mode3); + } + + if (pdata->pull_dis_mask & pdata->pullup_en_100k & + pdata->pullup_en_300k & pdata->pulldown_en_300k) + dev_warn(&client->dev, "Conflicting pull resistor config\n"); + + for (i = 0; i <= kpad->var->max_row_num; i++) { + unsigned val = 0, bit = (1 << i); + if (pdata->pullup_en_300k & bit) + val = 0; + else if (pdata->pulldown_en_300k & bit) + val = 1; + else if (pdata->pullup_en_100k & bit) + val = 2; + else if (pdata->pull_dis_mask & bit) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if (i == 3 || i == kpad->var->max_row_num) { + ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) + + (i >> 2), pull_mask); + pull_mask = 0; + } + } + + for (i = 0; i <= kpad->var->max_col_num; i++) { + unsigned val = 0, bit = 1 << (i + kpad->var->col_shift); + if (pdata->pullup_en_300k & bit) + val = 0; + else if (pdata->pulldown_en_300k & bit) + val = 1; + else if (pdata->pullup_en_100k & bit) + val = 2; + else if (pdata->pull_dis_mask & bit) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if (i == 3 || i == kpad->var->max_col_num) { + ret |= adp5589_write(client, + reg(ADP5585_RPULL_CONFIG_C) + + (i >> 2), pull_mask); + pull_mask = 0; + } + } + + if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) { + ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_A), + adp5589_get_evcode(kpad, + pdata->reset1_key_1)); + ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_B), + adp5589_get_evcode(kpad, + pdata->reset1_key_2)); + ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_C), + adp5589_get_evcode(kpad, + pdata->reset1_key_3)); + kpad->extend_cfg |= R4_EXTEND_CFG; + } + + if (pdata->reset2_key_1 && pdata->reset2_key_2) { + ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_A), + adp5589_get_evcode(kpad, + pdata->reset2_key_1)); + ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_B), + adp5589_get_evcode(kpad, + pdata->reset2_key_2)); + kpad->extend_cfg |= C4_EXTEND_CFG; + } + + if (kpad->extend_cfg) { + ret |= adp5589_write(client, reg(ADP5589_RESET_CFG), + pdata->reset_cfg); + ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_D), + kpad->extend_cfg); + } + + ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_A), + pdata->debounce_dis_mask & kpad->var->row_mask); + + ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_B), + (pdata->debounce_dis_mask >> kpad->var->col_shift) + & kpad->var->col_mask); + + if (!kpad->is_adp5585) + ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_C), + (pdata->debounce_dis_mask >> 16) & 0xFF); + + ret |= adp5589_write(client, reg(ADP5589_POLL_PTIME_CFG), + pdata->scan_cycle_time & PTIME_MASK); + ret |= adp5589_write(client, ADP5589_5_INT_STATUS, + (kpad->is_adp5585 ? 0 : LOGIC2_INT) | + LOGIC1_INT | OVRFLOW_INT | + (kpad->is_adp5585 ? 0 : LOCK_INT) | + GPI_INT | EVENT_INT); /* Status is W1C */ + + ret |= adp5589_write(client, reg(ADP5589_GENERAL_CFG), + INT_CFG | OSC_EN | CORE_CLK(3)); + ret |= adp5589_write(client, reg(ADP5589_INT_EN), + OVRFLOW_IEN | GPI_IEN | EVENT_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static void adp5589_report_switch_state(struct adp5589_kpad *kpad) +{ + int gpi_stat_tmp, pin_loc; + int i; + int gpi_stat1 = adp5589_read(kpad->client, + kpad->var->reg(ADP5589_GPI_STATUS_A)); + int gpi_stat2 = adp5589_read(kpad->client, + kpad->var->reg(ADP5589_GPI_STATUS_B)); + int gpi_stat3 = !kpad->is_adp5585 ? + adp5589_read(kpad->client, ADP5589_GPI_STATUS_C) : 0; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= kpad->var->gpi_pin_row_end) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - kpad->var->gpi_pin_row_base; + } else if ((pin - kpad->var->gpi_pin_col_base) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - kpad->var->gpi_pin_col_base; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - kpad->var->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 adp5589_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5589_kpad *kpad; + const struct adp5589_kpad_platform_data *pdata = + dev_get_platdata(&client->dev); + 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; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + switch (id->driver_data) { + case ADP5585_02: + kpad->adp5585_support_row5 = true; + case ADP5585_01: + kpad->is_adp5585 = true; + kpad->var = &const_adp5585; + break; + case ADP5589: + kpad->var = &const_adp5589; + break; + } + + if (!((pdata->keypad_en_mask & kpad->var->row_mask) && + (pdata->keypad_en_mask >> kpad->var->col_shift)) || + !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + error = -EINVAL; + goto err_free_mem; + } + + if (pdata->keymapsize != kpad->var->keymapsize) { + dev_err(&client->dev, "invalid keymapsize\n"); + error = -EINVAL; + goto err_free_mem; + } + + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + error = -EINVAL; + goto err_free_mem; + } + + if (pdata->gpimapsize > kpad->var->gpimapsize_max) { + dev_err(&client->dev, "invalid gpimapsize\n"); + error = -EINVAL; + goto err_free_mem; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < kpad->var->gpi_pin_base || + pin > kpad->var->gpi_pin_end) { + dev_err(&client->dev, "invalid gpi pin data\n"); + error = -EINVAL; + goto err_free_mem; + } + + if ((1 << (pin - kpad->var->gpi_pin_row_base)) & + pdata->keypad_en_mask) { + dev_err(&client->dev, "invalid gpi row/col data\n"); + error = -EINVAL; + goto err_free_mem; + } + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + error = -EINVAL; + goto err_free_mem; + } + + input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + + ret = adp5589_read(client, ADP5589_5_ID); + if (ret < 0) { + error = ret; + goto err_free_input; + } + + revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK; + + input->name = client->name; + input->phys = "adp5589-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++) + if (kpad->keycode[i] <= KEY_MAX) + __set_bit(kpad->keycode[i], 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_input; + } + + error = request_threaded_irq(client->irq, NULL, adp5589_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5589_setup(kpad); + if (error) + goto err_free_irq; + + if (kpad->gpimapsize) + adp5589_report_switch_state(kpad); + + error = adp5589_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_input: + input_free_device(input); +err_free_mem: + kfree(kpad); + + return error; +} + +static int adp5589_remove(struct i2c_client *client) +{ + struct adp5589_kpad *kpad = i2c_get_clientdata(client); + + adp5589_write(client, kpad->var->reg(ADP5589_GENERAL_CFG), 0); + free_irq(client->irq, kpad); + input_unregister_device(kpad->input); + adp5589_gpio_remove(kpad); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int adp5589_suspend(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5589_resume(struct device *dev) +{ + struct adp5589_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; +} +#endif + +static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume); + +static const struct i2c_device_id adp5589_id[] = { + {"adp5589-keys", ADP5589}, + {"adp5585-keys", ADP5585_01}, + {"adp5585-02-keys", ADP5585_02}, /* Adds ROW5 to ADP5585 */ + {} +}; + +MODULE_DEVICE_TABLE(i2c, adp5589_id); + +static struct i2c_driver adp5589_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pm = &adp5589_dev_pm_ops, + }, + .probe = adp5589_probe, + .remove = adp5589_remove, + .id_table = adp5589_id, +}; + +module_i2c_driver(adp5589_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP5589/ADP5585 Keypad driver"); diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index 35149ec455a..096d6067ae1 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -35,6 +35,7 @@ #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> @@ -154,10 +155,9 @@ static const char *amikbd_messages[8] = { [7] = KERN_WARNING "amikbd: keyboard interrupt\n" }; -static struct input_dev *amikbd_dev; - -static irqreturn_t amikbd_interrupt(int irq, void *dummy) +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) */ @@ -170,47 +170,42 @@ static irqreturn_t amikbd_interrupt(int irq, void *dummy) if (scancode < 0x78) { /* scancodes < 0x78 are keys */ if (scancode == 98) { /* CapsLock is a toggle switch key on Amiga */ - input_report_key(amikbd_dev, scancode, 1); - input_report_key(amikbd_dev, scancode, 0); + input_report_key(dev, scancode, 1); + input_report_key(dev, scancode, 0); } else { - input_report_key(amikbd_dev, scancode, down); + input_report_key(dev, scancode, down); } - input_sync(amikbd_dev); + input_sync(dev); } else /* scancodes >= 0x78 are error codes */ printk(amikbd_messages[scancode - 0x78]); return IRQ_HANDLED; } -static int __init amikbd_init(void) +static int __init amikbd_probe(struct platform_device *pdev) { + struct input_dev *dev; int i, j, err; - if (!AMIGAHW_PRESENT(AMI_KEYBOARD)) - return -ENODEV; - - if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb")) - return -EBUSY; - - amikbd_dev = input_allocate_device(); - if (!amikbd_dev) { - printk(KERN_ERR "amikbd: not enough memory for input device\n"); - err = -ENOMEM; - goto fail1; + dev = input_allocate_device(); + if (!dev) { + dev_err(&pdev->dev, "Not enough memory for input device\n"); + return -ENOMEM; } - amikbd_dev->name = "Amiga Keyboard"; - amikbd_dev->phys = "amikbd/input0"; - amikbd_dev->id.bustype = BUS_AMIGA; - amikbd_dev->id.vendor = 0x0001; - amikbd_dev->id.product = 0x0001; - amikbd_dev->id.version = 0x0100; + 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; - amikbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); for (i = 0; i < 0x78; i++) - set_bit(i, amikbd_dev->keybit); + set_bit(i, dev->keybit); for (i = 0; i < MAX_NR_KEYMAPS; i++) { static u_short temp_map[NR_KEYS] __initdata; @@ -229,30 +224,41 @@ static int __init amikbd_init(void) memcpy(key_maps[i], temp_map, sizeof(temp_map)); } ciaa.cra &= ~0x41; /* serial data in, turn off TA */ - if (request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", - amikbd_interrupt)) { - err = -EBUSY; + err = request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", + dev); + if (err) goto fail2; - } - err = input_register_device(amikbd_dev); + err = input_register_device(dev); if (err) goto fail3; + platform_set_drvdata(pdev, dev); + return 0; - fail3: free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt); - fail2: input_free_device(amikbd_dev); - fail1: release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100); + fail3: free_irq(IRQ_AMIGA_CIAA_SP, dev); + fail2: input_free_device(dev); return err; } -static void __exit amikbd_exit(void) +static int __exit amikbd_remove(struct platform_device *pdev) { - free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt); - input_unregister_device(amikbd_dev); - release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100); + struct input_dev *dev = platform_get_drvdata(pdev); + + free_irq(IRQ_AMIGA_CIAA_SP, dev); + input_unregister_device(dev); + return 0; } -module_init(amikbd_init); -module_exit(amikbd_exit); +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"); diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c index 1839194ea98..10bcd4ae540 100644 --- a/drivers/input/keyboard/atakbd.c +++ b/drivers/input/keyboard/atakbd.c @@ -223,8 +223,9 @@ static int __init atakbd_init(void) return -ENODEV; // need to init core driver if not already done so - if (atari_keyb_init()) - return -ENODEV; + error = atari_keyb_init(); + if (error) + return error; atakbd_dev = input_allocate_device(); if (!atakbd_dev) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 7b4056292ea..2dd1d0dd4f7 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -40,29 +40,33 @@ module_param_named(set, atkbd_set, int, 0); MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)"); #if defined(__i386__) || defined(__x86_64__) || defined(__hppa__) -static int atkbd_reset; +static bool atkbd_reset; #else -static int atkbd_reset = 1; +static bool atkbd_reset = true; #endif module_param_named(reset, atkbd_reset, bool, 0); MODULE_PARM_DESC(reset, "Reset keyboard during initialization"); -static int atkbd_softrepeat; +static bool atkbd_softrepeat; module_param_named(softrepeat, atkbd_softrepeat, bool, 0); MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat"); -static int atkbd_softraw = 1; +static bool atkbd_softraw = true; module_param_named(softraw, atkbd_softraw, bool, 0); MODULE_PARM_DESC(softraw, "Use software generated rawmode"); -static int atkbd_scroll; +static bool atkbd_scroll; module_param_named(scroll, atkbd_scroll, bool, 0); MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards"); -static int atkbd_extra; +static bool atkbd_extra; module_param_named(extra, atkbd_extra, bool, 0); MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards"); +static bool atkbd_terminal; +module_param_named(terminal, atkbd_terminal, bool, 0); +MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); + /* * Scancode to keycode tables. These are just the default setting, and * are loadable via a userland utility. @@ -136,7 +140,8 @@ static const unsigned short atkbd_unxlate_table[128] = { #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */ #define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */ -#define ATKBD_CMD_SETALL_MBR 0x00fa +#define ATKBD_CMD_SETALL_MB 0x00f8 /* Set all keys to give break codes */ +#define ATKBD_CMD_SETALL_MBR 0x00fa /* ... and repeat */ #define ATKBD_CMD_RESET_BAT 0x02ff #define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_EX_ENABLE 0x10ea @@ -153,16 +158,16 @@ static const unsigned short atkbd_unxlate_table[128] = { #define ATKBD_RET_HANGEUL 0xf2 #define ATKBD_RET_ERR 0xff -#define ATKBD_KEY_UNKNOWN 0 +#define ATKBD_KEY_UNKNOWN 0 #define ATKBD_KEY_NULL 255 -#define ATKBD_SCR_1 254 -#define ATKBD_SCR_2 253 -#define ATKBD_SCR_4 252 -#define ATKBD_SCR_8 251 -#define ATKBD_SCR_CLICK 250 -#define ATKBD_SCR_LEFT 249 -#define ATKBD_SCR_RIGHT 248 +#define ATKBD_SCR_1 0xfffe +#define ATKBD_SCR_2 0xfffd +#define ATKBD_SCR_4 0xfffc +#define ATKBD_SCR_8 0xfffb +#define ATKBD_SCR_CLICK 0xfffa +#define ATKBD_SCR_LEFT 0xfff9 +#define ATKBD_SCR_RIGHT 0xfff8 #define ATKBD_SPECIAL ATKBD_SCR_RIGHT @@ -177,7 +182,7 @@ static const unsigned short atkbd_unxlate_table[128] = { #define ATKBD_XL_HANJA 0x20 static const struct { - unsigned char keycode; + unsigned short keycode; unsigned char set2; } atkbd_scroll_keys[] = { { ATKBD_SCR_1, 0xc5 }, @@ -206,18 +211,18 @@ struct atkbd { unsigned short keycode[ATKBD_KEYMAP_SIZE]; DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE); unsigned char set; - unsigned char translated; - unsigned char extra; - unsigned char write; - unsigned char softrepeat; - unsigned char softraw; - unsigned char scroll; - unsigned char enabled; + bool translated; + bool extra; + bool write; + bool softrepeat; + bool softraw; + bool scroll; + bool enabled; /* Accessed only from interrupt */ unsigned char emul; - unsigned char resend; - unsigned char release; + bool resend; + bool release; unsigned long xl_bit; unsigned int last; unsigned long time; @@ -238,6 +243,12 @@ static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); static void *atkbd_platform_fixup_data; static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int); +/* + * Certain keyboards to not like ATKBD_CMD_RESET_DIS and stop responding + * to many commands until full reset (ATKBD_CMD_RESET_BAT) is performed. + */ +static bool atkbd_skip_deactivate; + static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, ssize_t (*handler)(struct atkbd *, char *)); static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, @@ -301,18 +312,18 @@ static const unsigned int xl_table[] = { * Checks if we should mangle the scancode to extract 'release' bit * in translated mode. */ -static int atkbd_need_xlate(unsigned long xl_bit, unsigned char code) +static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) { int i; if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1) - return 0; + return false; for (i = 0; i < ARRAY_SIZE(xl_table); i++) if (code == xl_table[i]) return test_bit(i, &xl_bit); - return 1; + return true; } /* @@ -359,7 +370,7 @@ static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code */ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, - unsigned int flags) + unsigned int flags) { struct atkbd *atkbd = serio_get_drvdata(serio); struct input_dev *dev = atkbd->dev; @@ -368,20 +379,18 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, int value; unsigned short keycode; -#ifdef ATKBD_DEBUG - printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags); -#endif + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags); #if !defined(__i386__) && !defined (__x86_64__) if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) { - printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags); + dev_warn(&serio->dev, "Frame/parity error: %02x\n", flags); serio_write(serio, ATKBD_CMD_RESEND); - atkbd->resend = 1; + atkbd->resend = true; goto out; } if (!flags && data == ATKBD_RET_ACK) - atkbd->resend = 0; + atkbd->resend = false; #endif if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK)) @@ -412,32 +421,32 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, } switch (code) { - case ATKBD_RET_BAT: - atkbd->enabled = 0; - serio_reconnect(atkbd->ps2dev.serio); - goto out; - case ATKBD_RET_EMUL0: - atkbd->emul = 1; - goto out; - case ATKBD_RET_EMUL1: - atkbd->emul = 2; - goto out; - case ATKBD_RET_RELEASE: - atkbd->release = 1; - goto out; - case ATKBD_RET_ACK: - case ATKBD_RET_NAK: - if (printk_ratelimit()) - printk(KERN_WARNING "atkbd.c: Spurious %s on %s. " - "Some program might be trying access hardware directly.\n", - data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); - goto out; - case ATKBD_RET_ERR: - atkbd->err_count++; -#ifdef ATKBD_DEBUG - printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys); -#endif - goto out; + case ATKBD_RET_BAT: + atkbd->enabled = false; + serio_reconnect(atkbd->ps2dev.serio); + goto out; + case ATKBD_RET_EMUL0: + atkbd->emul = 1; + goto out; + case ATKBD_RET_EMUL1: + atkbd->emul = 2; + goto out; + case ATKBD_RET_RELEASE: + atkbd->release = true; + goto out; + case ATKBD_RET_ACK: + case ATKBD_RET_NAK: + if (printk_ratelimit()) + dev_warn(&serio->dev, + "Spurious %s on %s. " + "Some program might be trying to access hardware directly.\n", + data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); + goto out; + case ATKBD_RET_ERR: + atkbd->err_count++; + dev_dbg(&serio->dev, "Keyboard on %s reports too many keys pressed.\n", + serio->phys); + goto out; } code = atkbd_compat_scancode(atkbd, code); @@ -451,71 +460,72 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, input_event(dev, EV_MSC, MSC_SCAN, code); switch (keycode) { - case ATKBD_KEY_NULL: - break; - case ATKBD_KEY_UNKNOWN: - printk(KERN_WARNING - "atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n", - atkbd->release ? "released" : "pressed", - atkbd->translated ? "translated" : "raw", - atkbd->set, code, serio->phys); - printk(KERN_WARNING - "atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n", - code & 0x80 ? "e0" : "", code & 0x7f); - input_sync(dev); - break; - case ATKBD_SCR_1: - scroll = 1 - atkbd->release * 2; - break; - case ATKBD_SCR_2: - scroll = 2 - atkbd->release * 4; - break; - case ATKBD_SCR_4: - scroll = 4 - atkbd->release * 8; - break; - case ATKBD_SCR_8: - scroll = 8 - atkbd->release * 16; - break; - case ATKBD_SCR_CLICK: - click = !atkbd->release; - break; - case ATKBD_SCR_LEFT: - hscroll = -1; - break; - case ATKBD_SCR_RIGHT: - hscroll = 1; - break; - default: - if (atkbd->release) { - value = 0; - atkbd->last = 0; - } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) { - /* Workaround Toshiba laptop multiple keypress */ - value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2; - } else { - value = 1; - atkbd->last = code; - atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2; - } - - input_event(dev, EV_KEY, keycode, value); - input_sync(dev); + case ATKBD_KEY_NULL: + break; + case ATKBD_KEY_UNKNOWN: + dev_warn(&serio->dev, + "Unknown key %s (%s set %d, code %#x on %s).\n", + atkbd->release ? "released" : "pressed", + atkbd->translated ? "translated" : "raw", + atkbd->set, code, serio->phys); + dev_warn(&serio->dev, + "Use 'setkeycodes %s%02x <keycode>' to make it known.\n", + code & 0x80 ? "e0" : "", code & 0x7f); + input_sync(dev); + break; + case ATKBD_SCR_1: + scroll = 1; + break; + case ATKBD_SCR_2: + scroll = 2; + break; + case ATKBD_SCR_4: + scroll = 4; + break; + case ATKBD_SCR_8: + scroll = 8; + break; + case ATKBD_SCR_CLICK: + click = !atkbd->release; + break; + case ATKBD_SCR_LEFT: + hscroll = -1; + break; + case ATKBD_SCR_RIGHT: + hscroll = 1; + break; + default: + if (atkbd->release) { + value = 0; + atkbd->last = 0; + } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) { + /* Workaround Toshiba laptop multiple keypress */ + value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2; + } else { + value = 1; + atkbd->last = code; + atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2; + } - if (value && test_bit(code, atkbd->force_release_mask)) { - input_report_key(dev, keycode, 0); - input_sync(dev); - } + input_event(dev, EV_KEY, keycode, value); + input_sync(dev); + + if (value && test_bit(code, atkbd->force_release_mask)) { + input_report_key(dev, keycode, 0); + input_sync(dev); + } } if (atkbd->scroll) { if (click != -1) input_report_key(dev, BTN_MIDDLE, click); - input_report_rel(dev, REL_WHEEL, scroll); + input_report_rel(dev, REL_WHEEL, + atkbd->release ? -scroll : scroll); input_report_rel(dev, REL_HWHEEL, hscroll); input_sync(dev); } - atkbd->release = 0; + atkbd->release = false; out: return IRQ_HANDLED; } @@ -634,17 +644,18 @@ static int atkbd_event(struct input_dev *dev, switch (type) { - case EV_LED: - atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); - return 0; + case EV_LED: + atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); + return 0; - case EV_REP: - if (!atkbd->softrepeat) - atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT); - return 0; - } + case EV_REP: + if (!atkbd->softrepeat) + atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT); + return 0; - return -1; + default: + return -1; + } } /* @@ -655,7 +666,7 @@ static int atkbd_event(struct input_dev *dev, static inline void atkbd_enable(struct atkbd *atkbd) { serio_pause_rx(atkbd->ps2dev.serio); - atkbd->enabled = 1; + atkbd->enabled = true; serio_continue_rx(atkbd->ps2dev.serio); } @@ -667,10 +678,43 @@ static inline void atkbd_enable(struct atkbd *atkbd) static inline void atkbd_disable(struct atkbd *atkbd) { serio_pause_rx(atkbd->ps2dev.serio); - atkbd->enabled = 0; + atkbd->enabled = false; serio_continue_rx(atkbd->ps2dev.serio); } +static int atkbd_activate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + +/* + * Enable the keyboard to receive keystrokes. + */ + + if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { + dev_err(&ps2dev->serio->dev, + "Failed to enable keyboard on %s\n", + ps2dev->serio->phys); + return -1; + } + + return 0; +} + +/* + * atkbd_deactivate() resets and disables the keyboard from sending + * keystrokes. + */ + +static void atkbd_deactivate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + + if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS)) + dev_err(&ps2dev->serio->dev, + "Failed to deactivate keyboard on %s\n", + ps2dev->serio->phys); +} + /* * atkbd_probe() probes for an AT keyboard on a serio port. */ @@ -688,7 +732,9 @@ static int atkbd_probe(struct atkbd *atkbd) if (atkbd_reset) if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT)) - printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", ps2dev->serio->phys); + dev_warn(&ps2dev->serio->dev, + "keyboard reset failed on %s\n", + ps2dev->serio->phys); /* * Then we check the keyboard ID. We should get 0xab83 under normal conditions. @@ -718,11 +764,19 @@ static int atkbd_probe(struct atkbd *atkbd) atkbd->id = (param[0] << 8) | param[1]; if (atkbd->id == 0xaca1 && atkbd->translated) { - printk(KERN_ERR "atkbd.c: NCD terminal keyboards are only supported on non-translating\n"); - printk(KERN_ERR "atkbd.c: controllers. Use i8042.direct=1 to disable translation.\n"); + dev_err(&ps2dev->serio->dev, + "NCD terminal keyboards are only supported on non-translating controllers. " + "Use i8042.direct=1 to disable translation.\n"); return -1; } +/* + * Make sure nothing is coming from the keyboard and disturbs our + * internal state. + */ + if (!atkbd_skip_deactivate) + atkbd_deactivate(atkbd); + return 0; } @@ -737,7 +791,7 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra struct ps2dev *ps2dev = &atkbd->ps2dev; unsigned char param[2]; - atkbd->extra = 0; + atkbd->extra = false; /* * For known special keyboards we can go ahead and set the correct set. * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and @@ -756,11 +810,16 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra if (allow_extra) { param[0] = 0x71; if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) { - atkbd->extra = 1; + atkbd->extra = true; return 2; } } + if (atkbd_terminal) { + ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB); + return 3; + } + if (target_set != 3) return 2; @@ -812,23 +871,6 @@ static int atkbd_reset_state(struct atkbd *atkbd) return 0; } -static int atkbd_activate(struct atkbd *atkbd) -{ - struct ps2dev *ps2dev = &atkbd->ps2dev; - -/* - * Enable the keyboard to receive keystrokes. - */ - - if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { - printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n", - ps2dev->serio->phys); - return -1; - } - - return 0; -} - /* * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a * reboot. @@ -1070,9 +1112,13 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) input_dev->keycodesize = sizeof(unsigned short); input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); - for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) - if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL) + for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) { + if (atkbd->keycode[i] != KEY_RESERVED && + atkbd->keycode[i] != ATKBD_KEY_NULL && + atkbd->keycode[i] < ATKBD_SPECIAL) { __set_bit(atkbd->keycode[i], input_dev->keybit); + } + } } /* @@ -1100,12 +1146,14 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) switch (serio->id.type) { - case SERIO_8042_XL: - atkbd->translated = 1; - case SERIO_8042: - if (serio->write) - atkbd->write = 1; - break; + case SERIO_8042_XL: + atkbd->translated = true; + /* Fall through */ + + case SERIO_8042: + if (serio->write) + atkbd->write = true; + break; } atkbd->softraw = atkbd_softraw; @@ -1113,7 +1161,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) atkbd->scroll = atkbd_scroll; if (atkbd->softrepeat) - atkbd->softraw = 1; + atkbd->softraw = true; serio_set_drvdata(serio, atkbd); @@ -1130,7 +1178,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); atkbd_reset_state(atkbd); - atkbd_activate(atkbd); } else { atkbd->set = 2; @@ -1145,6 +1192,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) goto fail3; atkbd_enable(atkbd); + if (serio->write) + atkbd_activate(atkbd); err = input_register_device(atkbd->dev); if (err) @@ -1172,7 +1221,8 @@ static int atkbd_reconnect(struct serio *serio) int retval = -1; if (!atkbd || !drv) { - printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n"); + dev_dbg(&serio->dev, + "reconnect request, but serio is disconnected, ignoring...\n"); return -1; } @@ -1187,8 +1237,6 @@ static int atkbd_reconnect(struct serio *serio) if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) goto out; - atkbd_activate(atkbd); - /* * Restore LED state and repeat rate. While input core * will do this for us at resume time reconnect may happen @@ -1202,7 +1250,17 @@ static int atkbd_reconnect(struct serio *serio) } + /* + * Reset our state machine in case reconnect happened in the middle + * of multi-byte scancode. + */ + atkbd->xl_bit = 0; + atkbd->emul = 0; + atkbd_enable(atkbd); + if (atkbd->write) + atkbd_activate(atkbd); + retval = 0; out: @@ -1284,14 +1342,19 @@ static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; - unsigned char old_extra, old_set; + bool old_extra; + unsigned char old_set; if (!atkbd->write) return -EIO; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->extra != value) { @@ -1367,11 +1430,15 @@ static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; - unsigned char old_scroll; + bool old_scroll; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->scroll != value) { @@ -1411,14 +1478,19 @@ static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; - unsigned char old_set, old_extra; + unsigned char old_set; + bool old_extra; if (!atkbd->write) return -EIO; - if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3)) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value != 2 && value != 3) return -EINVAL; if (atkbd->set != value) { @@ -1461,14 +1533,18 @@ static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; - unsigned char old_softrepeat, old_softraw; + bool old_softrepeat, old_softraw; if (!atkbd->write) return -EIO; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->softrepeat != value) { @@ -1483,7 +1559,7 @@ static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t atkbd->dev = new_dev; atkbd->softrepeat = value; if (atkbd->softrepeat) - atkbd->softraw = 1; + atkbd->softraw = true; atkbd_set_device_attrs(atkbd); err = input_register_device(atkbd->dev); @@ -1511,11 +1587,15 @@ static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; - unsigned char old_softraw; + bool old_softraw; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; - if (strict_strtoul(buf, 10, &value) || value > 1) + if (value > 1) return -EINVAL; if (atkbd->softraw != value) { @@ -1555,14 +1635,20 @@ static int __init atkbd_setup_forced_release(const struct dmi_system_id *id) atkbd_platform_fixup = atkbd_apply_forced_release_keylist; atkbd_platform_fixup_data = id->driver_data; - return 0; + return 1; } static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) { atkbd_platform_scancode_fixup = id->driver_data; - return 0; + return 1; +} + +static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id) +{ + atkbd_skip_deactivate = true; + return 1; } static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { @@ -1702,6 +1788,20 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { .callback = atkbd_setup_scancode_fixup, .driver_data = atkbd_oqo_01plus_scancode_fixup, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + DMI_MATCH(DMI_PRODUCT_NAME, "LW25-B7HV"), + }, + .callback = atkbd_deactivate_fixup, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + DMI_MATCH(DMI_PRODUCT_NAME, "P1-J273B"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index fe376a27fe5..e6d46c5994d 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -30,10 +30,10 @@ #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> @@ -162,7 +162,7 @@ static irqreturn_t bfin_kpad_isr(int irq, void *dev_id) input_sync(input); if (bfin_kpad_get_keypressed(bf54x_kpad)) { - disable_irq(bf54x_kpad->irq); + disable_irq_nosync(bf54x_kpad->irq); bf54x_kpad->lastkey = key; mod_timer(&bf54x_kpad->timer, jiffies + bf54x_kpad->keyup_test_jiffies); @@ -176,10 +176,10 @@ static irqreturn_t bfin_kpad_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit bfin_kpad_probe(struct platform_device *pdev) +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 bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev); struct input_dev *input; int i, error; @@ -288,7 +288,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) __set_bit(EV_REP, input->evbit); for (i = 0; i < input->keycodemax; i++) - __set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit); + if (bf54x_kpad->keycode[i] <= KEY_MAX) + __set_bit(bf54x_kpad->keycode[i], input->keybit); __clear_bit(KEY_RESERVED, input->keybit); error = input_register_device(input); @@ -325,14 +326,13 @@ out0: kfree(bf54x_kpad->keycode); out: kfree(bf54x_kpad); - platform_set_drvdata(pdev, NULL); return error; } -static int __devexit bfin_kpad_remove(struct platform_device *pdev) +static int bfin_kpad_remove(struct platform_device *pdev) { - struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data; + struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev); struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); del_timer_sync(&bf54x_kpad->timer); @@ -345,7 +345,6 @@ static int __devexit bfin_kpad_remove(struct platform_device *pdev) kfree(bf54x_kpad->keycode); kfree(bf54x_kpad); - platform_set_drvdata(pdev, NULL); return 0; } @@ -383,29 +382,17 @@ static int bfin_kpad_resume(struct platform_device *pdev) # define bfin_kpad_resume NULL #endif -struct platform_driver bfin_kpad_device_driver = { +static struct platform_driver bfin_kpad_device_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, .probe = bfin_kpad_probe, - .remove = __devexit_p(bfin_kpad_remove), + .remove = bfin_kpad_remove, .suspend = bfin_kpad_suspend, .resume = bfin_kpad_resume, }; - -static int __init bfin_kpad_init(void) -{ - return platform_driver_register(&bfin_kpad_device_driver); -} - -static void __exit bfin_kpad_exit(void) -{ - platform_driver_unregister(&bfin_kpad_device_driver); -} - -module_init(bfin_kpad_init); -module_exit(bfin_kpad_exit); +module_platform_driver(bfin_kpad_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c new file mode 100644 index 00000000000..552b65c6e6b --- /dev/null +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -0,0 +1,207 @@ +/* + * Cirrus Logic CLPS711X Keypad driver + * + * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> + * + * 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/input.h> +#include <linux/input-polldev.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/sched.h> +#include <linux/input/matrix_keypad.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/clps711x.h> + +#define CLPS711X_KEYPAD_COL_COUNT 8 + +struct clps711x_gpio_data { + struct gpio_desc *desc; + DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT); +}; + +struct clps711x_keypad_data { + struct regmap *syscon; + int row_count; + unsigned int row_shift; + struct clps711x_gpio_data *gpio_data; +}; + +static void clps711x_keypad_poll(struct input_polled_dev *dev) +{ + const unsigned short *keycodes = dev->input->keycode; + struct clps711x_keypad_data *priv = dev->private; + bool sync = false; + int col, row; + + for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) { + /* Assert column */ + regmap_update_bits(priv->syscon, SYSCON_OFFSET, + SYSCON1_KBDSCAN_MASK, + SYSCON1_KBDSCAN(8 + col)); + + /* Scan rows */ + for (row = 0; row < priv->row_count; row++) { + struct clps711x_gpio_data *data = &priv->gpio_data[row]; + bool state, state1; + + /* Read twice for protection against fluctuations */ + do { + state = gpiod_get_value_cansleep(data->desc); + cond_resched(); + state1 = gpiod_get_value_cansleep(data->desc); + } while (state != state1); + + if (test_bit(col, data->last_state) != state) { + int code = MATRIX_SCAN_CODE(row, col, + priv->row_shift); + + if (state) { + set_bit(col, data->last_state); + input_event(dev->input, EV_MSC, + MSC_SCAN, code); + } else { + clear_bit(col, data->last_state); + } + + if (keycodes[code]) + input_report_key(dev->input, + keycodes[code], state); + sync = true; + } + } + + /* Set all columns to low */ + regmap_update_bits(priv->syscon, SYSCON_OFFSET, + SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1)); + } + + if (sync) + input_sync(dev->input); +} + +static int clps711x_keypad_probe(struct platform_device *pdev) +{ + struct clps711x_keypad_data *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct input_polled_dev *poll_dev; + u32 poll_interval; + int i, err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->syscon = + syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); + if (IS_ERR(priv->syscon)) + return PTR_ERR(priv->syscon); + + priv->row_count = of_gpio_named_count(np, "row-gpios"); + if (priv->row_count < 1) + return -EINVAL; + + priv->gpio_data = devm_kzalloc(dev, + sizeof(*priv->gpio_data) * priv->row_count, + GFP_KERNEL); + if (!priv->gpio_data) + return -ENOMEM; + + priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT); + + for (i = 0; i < priv->row_count; i++) { + struct clps711x_gpio_data *data = &priv->gpio_data[i]; + + data->desc = devm_gpiod_get_index(dev, "row", i); + if (!data->desc) + return -EINVAL; + + if (IS_ERR(data->desc)) + return PTR_ERR(data->desc); + + gpiod_direction_input(data->desc); + } + + err = of_property_read_u32(np, "poll-interval", &poll_interval); + if (err) + return err; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) + return -ENOMEM; + + poll_dev->private = priv; + poll_dev->poll = clps711x_keypad_poll; + poll_dev->poll_interval = poll_interval; + poll_dev->input->name = pdev->name; + poll_dev->input->dev.parent = dev; + poll_dev->input->id.bustype = BUS_HOST; + poll_dev->input->id.vendor = 0x0001; + poll_dev->input->id.product = 0x0001; + poll_dev->input->id.version = 0x0100; + + err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count, + CLPS711X_KEYPAD_COL_COUNT, + NULL, poll_dev->input); + if (err) + goto out_err; + + input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN); + if (of_property_read_bool(np, "autorepeat")) + __set_bit(EV_REP, poll_dev->input->evbit); + + platform_set_drvdata(pdev, poll_dev); + + /* Set all columns to low */ + regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK, + SYSCON1_KBDSCAN(1)); + + err = input_register_polled_device(poll_dev); + if (err) + goto out_err; + + return 0; + +out_err: + input_free_polled_device(poll_dev); + return err; +} + +static int clps711x_keypad_remove(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev = platform_get_drvdata(pdev); + + input_unregister_polled_device(poll_dev); + input_free_polled_device(poll_dev); + + return 0; +} + +static const struct of_device_id clps711x_keypad_of_match[] = { + { .compatible = "cirrus,clps711x-keypad", }, + { } +}; +MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); + +static struct platform_driver clps711x_keypad_driver = { + .driver = { + .name = "clps711x-keypad", + .owner = THIS_MODULE, + .of_match_table = clps711x_keypad_of_match, + }, + .probe = clps711x_keypad_probe, + .remove = clps711x_keypad_remove, +}; +module_platform_driver(clps711x_keypad_driver); + +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c deleted file mode 100644 index 634af6a8e6b..00000000000 --- a/drivers/input/keyboard/corgikbd.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Keyboard driver for Sharp Corgi models (SL-C7xx) - * - * Copyright (c) 2004-2005 Richard Purdie - * - * Based on xtkbd.c/locomkbd.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/delay.h> -#include <linux/platform_device.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/slab.h> - -#include <mach/corgi.h> -#include <mach/pxa2xx-gpio.h> -#include <asm/hardware/scoop.h> - -#define KB_ROWS 8 -#define KB_COLS 12 -#define KB_ROWMASK(r) (1 << (r)) -#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 ) -/* zero code, 124 scancodes */ -#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 ) - -#define SCAN_INTERVAL (50) /* ms */ -#define HINGE_SCAN_INTERVAL (250) /* ms */ - -#define CORGI_KEY_CALENDER KEY_F1 -#define CORGI_KEY_ADDRESS KEY_F2 -#define CORGI_KEY_FN KEY_F3 -#define CORGI_KEY_CANCEL KEY_F4 -#define CORGI_KEY_OFF KEY_SUSPEND -#define CORGI_KEY_EXOK KEY_F5 -#define CORGI_KEY_EXCANCEL KEY_F6 -#define CORGI_KEY_EXJOGDOWN KEY_F7 -#define CORGI_KEY_EXJOGUP KEY_F8 -#define CORGI_KEY_JAP1 KEY_LEFTCTRL -#define CORGI_KEY_JAP2 KEY_LEFTALT -#define CORGI_KEY_MAIL KEY_F10 -#define CORGI_KEY_OK KEY_F11 -#define CORGI_KEY_MENU KEY_F12 - -static unsigned char corgikbd_keycode[NR_SCANCODES] = { - 0, /* 0 */ - 0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, /* 1-16 */ - 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0, /* 17-32 */ - KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ - CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ - CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */ - CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ - KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ - CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */ -}; - - -struct corgikbd { - unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)]; - struct input_dev *input; - - spinlock_t lock; - struct timer_list timer; - struct timer_list htimer; - - unsigned int suspended; - unsigned long suspend_jiffies; -}; - -#define KB_DISCHARGE_DELAY 10 -#define KB_ACTIVATE_DELAY 10 - -/* Helper functions for reading the keyboard matrix - * Note: We should really be using the generic gpio functions to alter - * GPDR but it requires a function call per GPIO bit which is - * excessive when we need to access 12 bits at once, multiple times. - * These functions must be called within local_irq_save()/local_irq_restore() - * or similar. - */ -static inline void corgikbd_discharge_all(void) -{ - /* STROBE All HiZ */ - GPCR2 = CORGI_GPIO_ALL_STROBE_BIT; - GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT; -} - -static inline void corgikbd_activate_all(void) -{ - /* STROBE ALL -> High */ - GPSR2 = CORGI_GPIO_ALL_STROBE_BIT; - GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT; - - udelay(KB_DISCHARGE_DELAY); - - /* Clear any interrupts we may have triggered when altering the GPIO lines */ - GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT; - GEDR2 = CORGI_GPIO_LOW_SENSE_BIT; -} - -static inline void corgikbd_activate_col(int col) -{ - /* STROBE col -> High, not col -> HiZ */ - GPSR2 = CORGI_GPIO_STROBE_BIT(col); - GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col); -} - -static inline void corgikbd_reset_col(int col) -{ - /* STROBE col -> Low */ - GPCR2 = CORGI_GPIO_STROBE_BIT(col); - /* STROBE col -> out, not col -> HiZ */ - GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col); -} - -#define GET_ROWS_STATUS(c) (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT)) - -/* - * The corgi keyboard only generates interrupts when a key is pressed. - * When a key is pressed, we enable a timer which then scans the - * keyboard to detect when the key is released. - */ - -/* Scan the hardware keyboard and push any changes up through the input layer */ -static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data) -{ - unsigned int row, col, rowd; - unsigned long flags; - unsigned int num_pressed; - - if (corgikbd_data->suspended) - return; - - spin_lock_irqsave(&corgikbd_data->lock, flags); - - num_pressed = 0; - for (col = 0; col < KB_COLS; col++) { - /* - * Discharge the output driver capacitatance - * in the keyboard matrix. (Yes it is significant..) - */ - - corgikbd_discharge_all(); - udelay(KB_DISCHARGE_DELAY); - - corgikbd_activate_col(col); - udelay(KB_ACTIVATE_DELAY); - - rowd = GET_ROWS_STATUS(col); - for (row = 0; row < KB_ROWS; row++) { - unsigned int scancode, pressed; - - scancode = SCANCODE(row, col); - pressed = rowd & KB_ROWMASK(row); - - input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed); - - if (pressed) - num_pressed++; - - if (pressed && (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF) - && time_after(jiffies, corgikbd_data->suspend_jiffies + HZ)) { - input_event(corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1); - corgikbd_data->suspend_jiffies=jiffies; - } - } - corgikbd_reset_col(col); - } - - corgikbd_activate_all(); - - input_sync(corgikbd_data->input); - - /* if any keys are pressed, enable the timer */ - if (num_pressed) - mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL)); - - spin_unlock_irqrestore(&corgikbd_data->lock, flags); -} - -/* - * corgi keyboard interrupt handler. - */ -static irqreturn_t corgikbd_interrupt(int irq, void *dev_id) -{ - struct corgikbd *corgikbd_data = dev_id; - - if (!timer_pending(&corgikbd_data->timer)) { - /** wait chattering delay **/ - udelay(20); - corgikbd_scankeyboard(corgikbd_data); - } - - return IRQ_HANDLED; -} - -/* - * corgi timer checking for released keys - */ -static void corgikbd_timer_callback(unsigned long data) -{ - struct corgikbd *corgikbd_data = (struct corgikbd *) data; - corgikbd_scankeyboard(corgikbd_data); -} - -/* - * The hinge switches generate no interrupt so they need to be - * monitored by a timer. - * - * We debounce the switches and pass them to the input system. - * - * gprr == 0x00 - Keyboard with Landscape Screen - * 0x08 - No Keyboard with Portrait Screen - * 0x0c - Keyboard and Screen Closed - */ - -#define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x)) -#define HINGE_STABLE_COUNT 2 -static int sharpsl_hinge_state; -static int hinge_count; - -static void corgikbd_hinge_timer(unsigned long data) -{ - struct corgikbd *corgikbd_data = (struct corgikbd *) data; - unsigned long gprr; - unsigned long flags; - - gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB); - gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0); - if (gprr != sharpsl_hinge_state) { - hinge_count = 0; - sharpsl_hinge_state = gprr; - } else if (hinge_count < HINGE_STABLE_COUNT) { - hinge_count++; - if (hinge_count >= HINGE_STABLE_COUNT) { - spin_lock_irqsave(&corgikbd_data->lock, flags); - - input_report_switch(corgikbd_data->input, SW_LID, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0)); - input_report_switch(corgikbd_data->input, SW_TABLET_MODE, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0)); - input_report_switch(corgikbd_data->input, SW_HEADPHONE_INSERT, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0)); - input_sync(corgikbd_data->input); - - spin_unlock_irqrestore(&corgikbd_data->lock, flags); - } - } - mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); -} - -#ifdef CONFIG_PM -static int corgikbd_suspend(struct platform_device *dev, pm_message_t state) -{ - int i; - struct corgikbd *corgikbd = platform_get_drvdata(dev); - - corgikbd->suspended = 1; - /* strobe 0 is the power key so this can't be made an input for - powersaving therefore i = 1 */ - for (i = 1; i < CORGI_KEY_STROBE_NUM; i++) - pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_IN); - - return 0; -} - -static int corgikbd_resume(struct platform_device *dev) -{ - int i; - struct corgikbd *corgikbd = platform_get_drvdata(dev); - - for (i = 1; i < CORGI_KEY_STROBE_NUM; i++) - pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); - - /* Upon resume, ignore the suspend key for a short while */ - corgikbd->suspend_jiffies=jiffies; - corgikbd->suspended = 0; - - return 0; -} -#else -#define corgikbd_suspend NULL -#define corgikbd_resume NULL -#endif - -static int __devinit corgikbd_probe(struct platform_device *pdev) -{ - struct corgikbd *corgikbd; - struct input_dev *input_dev; - int i, err = -ENOMEM; - - corgikbd = kzalloc(sizeof(struct corgikbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!corgikbd || !input_dev) - goto fail; - - platform_set_drvdata(pdev, corgikbd); - - corgikbd->input = input_dev; - spin_lock_init(&corgikbd->lock); - - /* Init Keyboard rescan timer */ - init_timer(&corgikbd->timer); - corgikbd->timer.function = corgikbd_timer_callback; - corgikbd->timer.data = (unsigned long) corgikbd; - - /* Init Hinge Timer */ - init_timer(&corgikbd->htimer); - corgikbd->htimer.function = corgikbd_hinge_timer; - corgikbd->htimer.data = (unsigned long) corgikbd; - - corgikbd->suspend_jiffies=jiffies; - - memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode)); - - input_dev->name = "Corgi Keyboard"; - input_dev->phys = "corgikbd/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &pdev->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | - BIT_MASK(EV_PWR) | BIT_MASK(EV_SW); - input_dev->keycode = corgikbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); - input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode); - - for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++) - set_bit(corgikbd->keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); - set_bit(SW_LID, input_dev->swbit); - set_bit(SW_TABLET_MODE, input_dev->swbit); - set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); - - err = input_register_device(corgikbd->input); - if (err) - goto fail; - - mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); - - /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ - for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) { - pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN); - if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "corgikbd", corgikbd)) - printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i); - } - - /* Set Strobe lines as outputs - set high */ - for (i = 0; i < CORGI_KEY_STROBE_NUM; i++) - pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); - - /* Setup the headphone jack as an input */ - pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN); - - return 0; - - fail: input_free_device(input_dev); - kfree(corgikbd); - return err; -} - -static int __devexit corgikbd_remove(struct platform_device *pdev) -{ - int i; - struct corgikbd *corgikbd = platform_get_drvdata(pdev); - - for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) - free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd); - - del_timer_sync(&corgikbd->htimer); - del_timer_sync(&corgikbd->timer); - - input_unregister_device(corgikbd->input); - - kfree(corgikbd); - - return 0; -} - -static struct platform_driver corgikbd_driver = { - .probe = corgikbd_probe, - .remove = __devexit_p(corgikbd_remove), - .suspend = corgikbd_suspend, - .resume = corgikbd_resume, - .driver = { - .name = "corgi-keyboard", - .owner = THIS_MODULE, - }, -}; - -static int __init corgikbd_init(void) -{ - return platform_driver_register(&corgikbd_driver); -} - -static void __exit corgikbd_exit(void) -{ - platform_driver_unregister(&corgikbd_driver); -} - -module_init(corgikbd_init); -module_exit(corgikbd_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); -MODULE_DESCRIPTION("Corgi Keyboard Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:corgi-keyboard"); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c new file mode 100644 index 00000000000..408379669d3 --- /dev/null +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -0,0 +1,341 @@ +/* + * 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/kernel.h> +#include <linux/notifier.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 + * @event_notifier: interrupt event notifier for transport devices + */ +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; + struct notifier_block notifier; +}; + + +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; + 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; + + 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); + + input_report_key(idev, keycodes[pos], + new_state); + } + } + ckdev->old_kb_state[col] = kb_state[col]; + } + input_sync(ckdev->idev); +} + +static int cros_ec_keyb_open(struct input_dev *dev) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + + return blocking_notifier_chain_register(&ckdev->ec->event_notifier, + &ckdev->notifier); +} + +static void cros_ec_keyb_close(struct input_dev *dev) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + + blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, + &ckdev->notifier); +} + +static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) +{ + return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE, + kb_state, ckdev->cols); +} + +static int cros_ec_keyb_work(struct notifier_block *nb, + unsigned long state, void *_notify) +{ + int ret; + struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, + notifier); + uint8_t kb_state[ckdev->cols]; + + ret = cros_ec_keyb_get_state(ckdev, kb_state); + if (ret >= 0) + cros_ec_keyb_process(ckdev, kb_state, ret); + + return NOTIFY_DONE; +} + +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; + + ckdev->ec = ec; + ckdev->notifier.notifier_call = cros_ec_keyb_work; + 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 +/* 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_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"); diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index d410d7a52f1..1559dc1cf95 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -5,7 +5,7 @@ * * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> * - * Intial Code: Sandeep Paulraj <s-paulraj@ti.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 @@ -30,12 +30,13 @@ #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 <mach/keyscan.h> +#include <linux/platform_data/keyscan-davinci.h> /* Key scan registers */ #define DAVINCI_KEYSCAN_KEYCTRL 0x0000 @@ -171,7 +172,7 @@ static int __init davinci_ks_probe(struct platform_device *pdev) struct input_dev *key_dev; struct resource *res, *mem; struct device *dev = &pdev->dev; - struct davinci_ks_platform_data *pdata = pdev->dev.platform_data; + struct davinci_ks_platform_data *pdata = dev_get_platdata(&pdev->dev); int error, i; if (pdata->device_enable) { @@ -270,7 +271,7 @@ static int __init davinci_ks_probe(struct platform_device *pdev) } error = request_irq(davinci_ks->irq, davinci_ks_interrupt, - IRQF_DISABLED, pdev->name, davinci_ks); + 0, pdev->name, davinci_ks); if (error < 0) { dev_err(dev, "unable to register davinci key scan interrupt\n"); goto fail5; @@ -302,7 +303,7 @@ fail1: return error; } -static int __devexit davinci_ks_remove(struct platform_device *pdev) +static int davinci_ks_remove(struct platform_device *pdev) { struct davinci_ks *davinci_ks = platform_get_drvdata(pdev); @@ -313,8 +314,6 @@ static int __devexit davinci_ks_remove(struct platform_device *pdev) iounmap(davinci_ks->base); release_mem_region(davinci_ks->pbase, davinci_ks->base_size); - platform_set_drvdata(pdev, NULL); - kfree(davinci_ks); return 0; @@ -325,20 +324,10 @@ static struct platform_driver davinci_ks_driver = { .name = "davinci_keyscan", .owner = THIS_MODULE, }, - .remove = __devexit_p(davinci_ks_remove), + .remove = davinci_ks_remove, }; -static int __init davinci_ks_init(void) -{ - return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe); -} -module_init(davinci_ks_init); - -static void __exit davinci_ks_exit(void) -{ - platform_driver_unregister(&davinci_ks_driver); -} -module_exit(davinci_ks_exit); +module_platform_driver_probe(davinci_ks_driver, davinci_ks_probe); MODULE_AUTHOR("Miguel Aguilar"); MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index e45740429f7..e59876212b8 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -20,14 +20,16 @@ * 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 <mach/ep93xx_keypad.h> +#include <linux/platform_data/keypad-ep93xx.h> /* * Keypad Interface Register offsets @@ -69,7 +71,7 @@ struct ep93xx_keypad { void __iomem *mmio_base; - unsigned int matrix_keycodes[EP93XX_MATRIX_SIZE]; + unsigned short keycodes[EP93XX_MATRIX_SIZE]; int key1; int key2; @@ -79,24 +81,6 @@ struct ep93xx_keypad { bool enabled; }; -static void ep93xx_keypad_build_keycode(struct ep93xx_keypad *keypad) -{ - struct ep93xx_keypad_platform_data *pdata = keypad->pdata; - struct input_dev *input_dev = keypad->input_dev; - unsigned int *key; - int i; - - key = &pdata->matrix_key_map[0]; - for (i = 0; i < pdata->matrix_key_map_size; i++, key++) { - int row = KEY_ROW(*key); - int col = KEY_COL(*key); - int code = KEY_VAL(*key); - - keypad->matrix_keycodes[(row << 3) + col] = code; - __set_bit(code, input_dev->keybit); - } -} - static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id) { struct ep93xx_keypad *keypad = dev_id; @@ -107,10 +91,10 @@ static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id) status = __raw_readl(keypad->mmio_base + KEY_REG); keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT; - key1 = keypad->matrix_keycodes[keycode]; + key1 = keypad->keycodes[keycode]; keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT; - key2 = keypad->matrix_keycodes[keycode]; + key2 = keypad->keycodes[keycode]; if (status & KEY_REG_2KEYS) { if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1) @@ -198,16 +182,10 @@ static void ep93xx_keypad_close(struct input_dev *pdev) } -#ifdef CONFIG_PM -/* - * NOTE: I don't know if this is correct, or will work on the ep93xx. - * - * None of the existing ep93xx drivers have power management support. - * But, this is basically what the pxa27x_keypad driver does. - */ -static int ep93xx_keypad_suspend(struct platform_device *pdev, - pm_message_t state) +#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; @@ -226,8 +204,9 @@ static int ep93xx_keypad_suspend(struct platform_device *pdev, return 0; } -static int ep93xx_keypad_resume(struct platform_device *pdev) +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; @@ -248,14 +227,15 @@ static int ep93xx_keypad_resume(struct platform_device *pdev) return 0; } -#else /* !CONFIG_PM */ -#define ep93xx_keypad_suspend NULL -#define ep93xx_keypad_resume NULL -#endif /* !CONFIG_PM */ +#endif + +static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops, + ep93xx_keypad_suspend, ep93xx_keypad_resume); -static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) +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; @@ -264,12 +244,18 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) if (!keypad) return -ENOMEM; - keypad->pdata = pdev->dev.platform_data; + keypad->pdata = dev_get_platdata(&pdev->dev); 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; @@ -317,21 +303,19 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) input_dev->open = ep93xx_keypad_open; input_dev->close = ep93xx_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->matrix_keycodes; - input_dev->keycodesize = sizeof(keypad->matrix_keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->matrix_keycodes); - input_set_drvdata(input_dev, keypad); + err = matrix_keypad_build_keymap(keymap_data, NULL, + EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS, + keypad->keycodes, input_dev); + if (err) + goto failed_free_dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY); if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT) - input_dev->evbit[0] |= BIT_MASK(EV_REP); - - ep93xx_keypad_build_keycode(keypad); - platform_set_drvdata(pdev, keypad); + __set_bit(EV_REP, input_dev->evbit); + input_set_drvdata(input_dev, keypad); err = request_irq(keypad->irq, ep93xx_keypad_irq_handler, - IRQF_DISABLED, pdev->name, keypad); + 0, pdev->name, keypad); if (err) goto failed_free_dev; @@ -339,13 +323,13 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) 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); + free_irq(keypad->irq, keypad); failed_free_dev: input_free_device(input_dev); failed_put_clk: @@ -361,14 +345,12 @@ failed_free: return err; } -static int __devexit ep93xx_keypad_remove(struct platform_device *pdev) +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); + free_irq(keypad->irq, keypad); if (keypad->enabled) clk_disable(keypad->clk); @@ -392,25 +374,12 @@ static struct platform_driver ep93xx_keypad_driver = { .driver = { .name = "ep93xx-keypad", .owner = THIS_MODULE, + .pm = &ep93xx_keypad_pm_ops, }, .probe = ep93xx_keypad_probe, - .remove = __devexit_p(ep93xx_keypad_remove), - .suspend = ep93xx_keypad_suspend, - .resume = ep93xx_keypad_resume, + .remove = ep93xx_keypad_remove, }; - -static int __init ep93xx_keypad_init(void) -{ - return platform_driver_register(&ep93xx_keypad_driver); -} - -static void __exit ep93xx_keypad_exit(void) -{ - platform_driver_unregister(&ep93xx_keypad_driver); -} - -module_init(ep93xx_keypad_init); -module_exit(ep93xx_keypad_exit); +module_platform_driver(ep93xx_keypad_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c new file mode 100644 index 00000000000..69e85476337 --- /dev/null +++ b/drivers/input/keyboard/goldfish_events.c @@ -0,0 +1,193 @@ +/* + * 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/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"); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 1aff3b76eff..8c98e97f8e4 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -2,6 +2,7 @@ * 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 @@ -16,6 +17,7 @@ #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> @@ -24,136 +26,703 @@ #include <linux/gpio_keys.h> #include <linux/workqueue.h> #include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/spinlock.h> struct gpio_button_data { - struct gpio_keys_button *button; + 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]; }; -static void gpio_keys_report_event(struct gpio_button_data *bdata) +/* + * 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) { - struct gpio_keys_button *button = bdata->button; + 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(button->gpio) ? 1 : 0) ^ button->active_low; + int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; - input_event(input, type, button->code, !!state); + 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_work_func(struct work_struct *work) +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_report_event(bdata); + gpio_keys_gpio_report_event(bdata); + + if (bdata->button->wakeup) + pm_relax(bdata->input->dev.parent); } -static void gpio_keys_timer(unsigned long _data) +static void gpio_keys_gpio_timer(unsigned long _data) { - struct gpio_button_data *data = (struct gpio_button_data *)_data; + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; - schedule_work(&data->work); + schedule_work(&bdata->work); } -static irqreturn_t gpio_keys_isr(int irq, void *dev_id) +static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; - struct gpio_keys_button *button = bdata->button; - BUG_ON(irq != gpio_to_irq(button->gpio)); + BUG_ON(irq != bdata->irq); - if (button->debounce_interval) + if (bdata->button->wakeup) + pm_stay_awake(bdata->input->dev.parent); + if (bdata->timer_debounce) mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(button->debounce_interval)); + jiffies + msecs_to_jiffies(bdata->timer_debounce)); else schedule_work(&bdata->work); return IRQ_HANDLED; } -static int __devinit gpio_keys_setup_key(struct device *dev, - struct gpio_button_data *bdata, - struct gpio_keys_button *button) +static void gpio_keys_irq_timer(unsigned long _data) { - char *desc = button->desc ? button->desc : "gpio_keys"; - int irq, error; - - setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); - INIT_WORK(&bdata->work, gpio_keys_work_func); + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + struct input_dev *input = bdata->input; + unsigned long flags; - error = gpio_request(button->gpio, desc); - if (error < 0) { - dev_err(dev, "failed to request GPIO %d, error %d\n", - button->gpio, error); - goto fail2; + 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); +} - error = gpio_direction_input(button->gpio); - if (error < 0) { - dev_err(dev, "failed to configure" - " direction for GPIO %d, error %d\n", - button->gpio, error); - goto fail3; +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; } - 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 fail3; + 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 void gpio_keys_quiesce_key(void *data) +{ + struct gpio_button_data *bdata = data; + + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + + cancel_work_sync(&bdata->work); +} + +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; + int error; + + bdata->input = input; + bdata->button = button; + spin_lock_init(&bdata->lock); + + if (gpio_is_valid(button->gpio)) { + + error = devm_gpio_request_one(&pdev->dev, 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); + return error; + } + 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; } - error = request_irq(irq, gpio_keys_isr, - IRQF_SHARED | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - desc, bdata); + input_set_capability(input, button->type ?: EV_KEY, button->code); + + /* + * Install custom action to cancel debounce timer and + * workqueue item. + */ + error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata); if (error) { + dev_err(&pdev->dev, + "failed to register quiesce action, error: %d\n", + error); + return error; + } + + /* + * 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 = devm_request_any_context_irq(&pdev->dev, bdata->irq, + isr, irqflags, desc, bdata); + if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", - irq, error); - goto fail3; + bdata->irq, error); + return error; } return 0; +} -fail3: - gpio_free(button->gpio); -fail2: - 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 __devinit gpio_keys_probe(struct platform_device *pdev) +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) + return ERR_PTR(-ENODEV); + + nbuttons = of_get_child_count(node); + if (nbuttons == 0) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(dev, + sizeof(*pdata) + nbuttons * sizeof(*button), + GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + 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); + return ERR_PTR(error); + } + + 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); + return ERR_PTR(-EINVAL); + } + + 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) + return ERR_PTR(-EINVAL); + + return pdata; +} + +static const 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 int gpio_keys_probe(struct platform_device *pdev) { - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct gpio_keys_drvdata *ddata; 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; + size_t size; int i, error; int wakeup = 0; - ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + - pdata->nbuttons * sizeof(struct gpio_button_data), - GFP_KERNEL); - input = input_allocate_device(); - if (!ddata || !input) { + if (!pdata) { + pdata = gpio_keys_get_devtree_pdata(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + size = sizeof(struct gpio_keys_drvdata) + + pdata->nbuttons * sizeof(struct gpio_button_data); + ddata = devm_kzalloc(dev, size, GFP_KERNEL); + if (!ddata) { dev_err(dev, "failed to allocate state\n"); - error = -ENOMEM; - goto fail1; + return -ENOMEM; } + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + ddata->pdata = pdata; + ddata->input = input; + mutex_init(&ddata->disable_lock); + platform_set_drvdata(pdev, ddata); + input_set_drvdata(input, ddata); - input->name = pdev->name; + 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; @@ -164,98 +733,68 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) if (pdata->rep) __set_bit(EV_REP, input->evbit); - ddata->input = input; - for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; - unsigned int type = button->type ?: EV_KEY; - - bdata->input = input; - bdata->button = button; - error = gpio_keys_setup_key(dev, bdata, button); + error = gpio_keys_setup_key(pdev, input, bdata, button); if (error) - goto fail2; + return error; if (button->wakeup) wakeup = 1; + } - input_set_capability(input, type, button->code); + 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); + return error; } error = input_register_device(input); if (error) { - dev_err(dev, "Unable to register input device, " - "error: %d\n", error); - goto fail2; + dev_err(dev, "Unable to register input device, error: %d\n", + error); + goto err_remove_group; } - /* get current state of buttons */ - for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_report_event(&ddata->data[i]); - input_sync(input); - device_init_wakeup(&pdev->dev, wakeup); return 0; - fail2: - while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); - if (pdata->buttons[i].debounce_interval) - del_timer_sync(&ddata->data[i].timer); - cancel_work_sync(&ddata->data[i].work); - gpio_free(pdata->buttons[i].gpio); - } - - platform_set_drvdata(pdev, NULL); - fail1: - input_free_device(input); - kfree(ddata); - +err_remove_group: + sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); return error; } -static int __devexit gpio_keys_remove(struct platform_device *pdev) +static int gpio_keys_remove(struct platform_device *pdev) { - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - 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 < pdata->nbuttons; i++) { - int irq = gpio_to_irq(pdata->buttons[i].gpio); - free_irq(irq, &ddata->data[i]); - if (pdata->buttons[i].debounce_interval) - del_timer_sync(&ddata->data[i].timer); - cancel_work_sync(&ddata->data[i].work); - gpio_free(pdata->buttons[i].gpio); - } - - input_unregister_device(input); - return 0; } - -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int gpio_keys_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + struct input_dev *input = ddata->input; int i; - if (device_may_wakeup(&pdev->dev)) { - for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; - if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); - enable_irq_wake(irq); - } + 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; @@ -263,41 +802,42 @@ static int gpio_keys_suspend(struct device *dev) static int gpio_keys_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + struct input_dev *input = ddata->input; + int error = 0; int i; - for (i = 0; i < pdata->nbuttons; i++) { - - struct gpio_keys_button *button = &pdata->buttons[i]; - if (button->wakeup && device_may_wakeup(&pdev->dev)) { - int irq = gpio_to_irq(button->gpio); - disable_irq_wake(irq); + 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); } - - gpio_keys_report_event(&ddata->data[i]); + } else { + mutex_lock(&input->mutex); + if (input->users) + error = gpio_keys_open(input); + mutex_unlock(&input->mutex); } - input_sync(ddata->input); + if (error) + return error; + + gpio_keys_report_state(ddata); return 0; } - -static const struct dev_pm_ops gpio_keys_pm_ops = { - .suspend = gpio_keys_suspend, - .resume = gpio_keys_resume, -}; #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 = __devexit_p(gpio_keys_remove), + .remove = gpio_keys_remove, .driver = { .name = "gpio-keys", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &gpio_keys_pm_ops, -#endif + .of_match_table = of_match_ptr(gpio_keys_of_match), } }; @@ -311,10 +851,10 @@ static void __exit gpio_keys_exit(void) platform_driver_unregister(&gpio_keys_device_driver); } -module_init(gpio_keys_init); +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 CPU GPIOs"); +MODULE_DESCRIPTION("Keyboard driver for GPIOs"); MODULE_ALIAS("platform:gpio-keys"); diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c new file mode 100644 index 00000000000..432d36395f3 --- /dev/null +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -0,0 +1,319 @@ +/* + * 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/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.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 = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button), + GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + 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); + return ERR_PTR(error); + } + + 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); + return ERR_PTR(-EINVAL); + } + + 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) + return ERR_PTR(-EINVAL); + + return pdata; +} + +static const 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; + size_t size; + 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"); + return -EINVAL; + } + + size = sizeof(struct gpio_keys_polled_dev) + + pdata->nbuttons * sizeof(struct gpio_keys_button_data); + bdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!bdev) { + dev_err(dev, "no memory for private data\n"); + return -ENOMEM; + } + + poll_dev = devm_input_allocate_polled_device(&pdev->dev); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + return -ENOMEM; + } + + 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->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"); + return -EINVAL; + } + + error = devm_gpio_request_one(&pdev->dev, gpio, GPIOF_IN, + button->desc ? : DRV_NAME); + if (error) { + dev_err(dev, "unable to claim gpio %u, err=%d\n", + gpio, error); + return error; + } + + 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); + return error; + } + + /* 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; +} + +static struct platform_driver gpio_keys_polled_driver = { + .probe = gpio_keys_polled_probe, + .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); diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index c83f4b2ec7d..610a8af795a 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -36,7 +36,6 @@ #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> @@ -232,15 +231,16 @@ static void hil_dev_handle_ptr_events(struct hil_dev *ptr) if (absdev) { val = lo + (hi << 8); #ifdef TABLET_AUTOADJUST - if (val < dev->absmin[ABS_X + i]) - dev->absmin[ABS_X + i] = val; - if (val > dev->absmax[ABS_X + i]) - dev->absmax[ABS_X + i] = val; + 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 = dev->absmax[ABS_X + i] - val; + 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)); + val = (int) (((int8_t) lo) | ((int8_t) hi << 8)); if (i % 3) val *= -1; input_report_rel(dev, REL_X + i, val); @@ -387,9 +387,11 @@ static void hil_dev_pointer_setup(struct hil_dev *ptr) #ifdef TABLET_AUTOADJUST for (i = 0; i < ABS_MAX; i++) { - int diff = input_dev->absmax[ABS_X + i] / 10; - input_dev->absmin[ABS_X + i] += diff; - input_dev->absmax[ABS_X + i] -= diff; + 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 @@ -567,6 +569,8 @@ static struct serio_device_id hil_dev_ids[] = { { 0 } }; +MODULE_DEVICE_TABLE(serio, hil_dev_ids); + static struct serio_driver hil_serio_drv = { .driver = { .name = "hil_dev", @@ -578,15 +582,4 @@ static struct serio_driver hil_serio_drv = { .interrupt = hil_dev_interrupt }; -static int __init hil_dev_init(void) -{ - return serio_register_driver(&hil_serio_drv); -} - -static void __exit hil_dev_exit(void) -{ - serio_unregister_driver(&hil_serio_drv); -} - -module_init(hil_dev_init); -module_exit(hil_dev_exit); +module_serio_driver(hil_serio_drv); diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index 5f72440b50c..198dc07a1be 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -200,7 +200,7 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) /* initialize HIL */ -static int __devinit hil_keyb_init(void) +static int hil_keyb_init(void) { unsigned char c; unsigned int i, kbid; @@ -286,7 +286,7 @@ err1: return err; } -static void __devexit hil_keyb_exit(void) +static void hil_keyb_exit(void) { if (HIL_IRQ) free_irq(HIL_IRQ, hil_dev.dev_id); @@ -299,7 +299,7 @@ static void __devexit hil_keyb_exit(void) } #if defined(CONFIG_PARISC) -static int __devinit hil_probe_chip(struct parisc_device *dev) +static int hil_probe_chip(struct parisc_device *dev) { /* Only allow one HIL keyboard */ if (hil_dev.dev) @@ -320,7 +320,7 @@ static int __devinit hil_probe_chip(struct parisc_device *dev) return hil_keyb_init(); } -static int __devexit hil_remove_chip(struct parisc_device *dev) +static int hil_remove_chip(struct parisc_device *dev) { hil_keyb_exit(); @@ -341,7 +341,7 @@ static struct parisc_driver hil_driver = { .name = "hil", .id_table = hil_tbl, .probe = hil_probe_chip, - .remove = __devexit_p(hil_remove_chip), + .remove = hil_remove_chip, }; static int __init hil_init(void) diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c new file mode 100644 index 00000000000..8280cb16260 --- /dev/null +++ b/drivers/input/keyboard/imx_keypad.c @@ -0,0 +1,596 @@ +/* + * 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/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 const 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 = + dev_get_platdata(&pdev->dev); + 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 irq; + } + + 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); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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"); diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index 781fc610286..0ba4428da24 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -16,7 +16,7 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> +#include <linux/device.h> #include <linux/input.h> #include <linux/input-polldev.h> #include <linux/interrupt.h> @@ -24,6 +24,7 @@ #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> @@ -138,35 +139,35 @@ static void jornada_scan_keyb(unsigned char *s) }, *y = matrix_PDE; /* Save these control reg bits */ - dc_static = (ctrl_inw(PDCR) & (~0xcc0c)); - ec_static = (ctrl_inw(PECR) & (~0xf0cf)); + 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 */ - ctrl_outw((dc_static | *y++), PDCR); - ctrl_outw((ec_static | *y++), PECR); + __raw_writew((dc_static | *y++), PDCR); + __raw_writew((ec_static | *y++), PECR); udelay(5); /* Get scanline row */ - ctrl_outb(*t++, PDDR); - ctrl_outb(*t++, PEDR); + __raw_writeb(*t++, PDDR); + __raw_writeb(*t++, PEDR); udelay(50); /* Read data */ - *s++ = ctrl_inb(PCDR); - *s++ = ctrl_inb(PFDR); + *s++ = __raw_readb(PCDR); + *s++ = __raw_readb(PFDR); } /* Scan no lines */ - ctrl_outb(0xff, PDDR); - ctrl_outb(0xff, PEDR); + __raw_writeb(0xff, PDDR); + __raw_writeb(0xff, PEDR); /* Enable all scanlines */ - ctrl_outw((dc_static | (0x5555 & 0xcc0c)),PDCR); - ctrl_outw((ec_static | (0x5555 & 0xf0cf)),PECR); + __raw_writew((dc_static | (0x5555 & 0xcc0c)),PDCR); + __raw_writew((ec_static | (0x5555 & 0xf0cf)),PECR); /* Ignore extra keys and events */ - *s++ = ctrl_inb(PGDR); - *s++ = ctrl_inb(PHDR); + *s++ = __raw_readb(PGDR); + *s++ = __raw_readb(PHDR); } static void jornadakbd680_poll(struct input_polled_dev *dev) @@ -178,21 +179,22 @@ static void jornadakbd680_poll(struct input_polled_dev *dev) memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE); } -static int __devinit jornada680kbd_probe(struct platform_device *pdev) +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); + jornadakbd = devm_kzalloc(&pdev->dev, sizeof(struct jornadakbd), + GFP_KERNEL); if (!jornadakbd) return -ENOMEM; - poll_dev = input_allocate_polled_device(); + poll_dev = devm_input_allocate_polled_device(&pdev->dev); if (!poll_dev) { - error = -ENOMEM; - goto failed; + dev_err(&pdev->dev, "failed to allocate polled input device\n"); + return -ENOMEM; } platform_set_drvdata(pdev, jornadakbd); @@ -224,29 +226,10 @@ static int __devinit jornada680kbd_probe(struct platform_device *pdev) 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 __devexit 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); + if (error) { + dev_err(&pdev->dev, "failed to register polled input device\n"); + return error; + } return 0; } @@ -257,21 +240,8 @@ static struct platform_driver jornada680kbd_driver = { .owner = THIS_MODULE, }, .probe = jornada680kbd_probe, - .remove = __devexit_p(jornada680kbd_remove), }; - -static int __init jornada680kbd_init(void) -{ - return platform_driver_register(&jornada680kbd_driver); -} - -static void __exit jornada680kbd_exit(void) -{ - platform_driver_unregister(&jornada680kbd_driver); -} - -module_init(jornada680kbd_init); -module_exit(jornada680kbd_exit); +module_platform_driver(jornada680kbd_driver); MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver"); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index 4e016d82306..cd729d485e9 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -18,14 +18,15 @@ #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"); @@ -92,7 +93,7 @@ static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id) return IRQ_HANDLED; }; -static int __devinit jornada720_kbd_probe(struct platform_device *pdev) +static int jornada720_kbd_probe(struct platform_device *pdev) { struct jornadakbd *jornadakbd; struct input_dev *input_dev; @@ -128,7 +129,7 @@ static int __devinit jornada720_kbd_probe(struct platform_device *pdev) err = request_irq(IRQ_GPIO0, jornada720_kbd_interrupt, - IRQF_DISABLED | IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING, "jornadakbd", pdev); if (err) { printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n"); @@ -144,18 +145,16 @@ static int __devinit jornada720_kbd_probe(struct platform_device *pdev) 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 __devexit jornada720_kbd_remove(struct platform_device *pdev) +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); @@ -171,18 +170,6 @@ static struct platform_driver jornada720_kbd_driver = { .owner = THIS_MODULE, }, .probe = jornada720_kbd_probe, - .remove = __devexit_p(jornada720_kbd_remove), + .remove = jornada720_kbd_remove, }; - -static int __init jornada720_kbd_init(void) -{ - return platform_driver_register(&jornada720_kbd_driver); -} - -static void __exit jornada720_kbd_exit(void) -{ - platform_driver_unregister(&jornada720_kbd_driver); -} - -module_init(jornada720_kbd_init); -module_exit(jornada720_kbd_exit); +module_platform_driver(jornada720_kbd_driver); diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index fa9bb6d235e..9fcd9f1d5dc 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -65,7 +65,6 @@ #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> @@ -731,19 +730,4 @@ static struct serio_driver lkkbd_drv = { .interrupt = lkkbd_interrupt, }; -/* - * The functions for insering/removing us as a module. - */ -static int __init lkkbd_init(void) -{ - return serio_register_driver(&lkkbd_drv); -} - -static void __exit lkkbd_exit(void) -{ - serio_unregister_driver(&lkkbd_drv); -} - -module_init(lkkbd_init); -module_exit(lkkbd_exit); - +module_serio_driver(lkkbd_drv); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 574eda2a495..0b42118cbf8 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -30,7 +30,9 @@ #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. */ @@ -144,7 +146,6 @@ struct lm8323_chip { /* device lock */ struct mutex lock; struct i2c_client *client; - struct work_struct work; struct input_dev *idev; bool kp_enabled; bool pm_suspend; @@ -160,7 +161,6 @@ struct lm8323_chip { #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 work_to_lm8323(w) container_of(w, struct lm8323_chip, work) #define cdev_to_pwm(c) container_of(c, struct lm8323_pwm, cdev) #define work_to_pwm(w) container_of(w, struct lm8323_pwm, work) @@ -373,9 +373,9 @@ static void pwm_done(struct lm8323_pwm *pwm) * Bottom half: handle the interrupt by posting key events, or dealing with * errors appropriately. */ -static void lm8323_work(struct work_struct *work) +static irqreturn_t lm8323_irq(int irq, void *_lm) { - struct lm8323_chip *lm = work_to_lm8323(work); + struct lm8323_chip *lm = _lm; u8 ints; int i; @@ -398,7 +398,7 @@ static void lm8323_work(struct work_struct *work) lm8323_configure(lm); } for (i = 0; i < LM8323_NUM_PWMS; i++) { - if (ints & (1 << (INT_PWM1 + i))) { + if (ints & (INT_PWM1 << i)) { dev_vdbg(&lm->client->dev, "pwm%d engine completed\n", i); pwm_done(&lm->pwm[i]); @@ -407,16 +407,6 @@ static void lm8323_work(struct work_struct *work) } mutex_unlock(&lm->lock); -} - -/* - * We cannot use I2C in interrupt context, so we just schedule work. - */ -static irqreturn_t lm8323_irq(int irq, void *data) -{ - struct lm8323_chip *lm = data; - - schedule_work(&lm->work); return IRQ_HANDLED; } @@ -555,13 +545,12 @@ static ssize_t lm8323_pwm_store_time(struct device *dev, { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev); - int ret; - unsigned long time; + int ret, time; - ret = strict_strtoul(buf, 10, &time); + ret = kstrtoint(buf, 10, &time); /* Numbers only, please. */ if (ret) - return -EINVAL; + return ret; pwm->fade_time = time; @@ -623,9 +612,9 @@ static ssize_t lm8323_set_disable(struct device *dev, { struct lm8323_chip *lm = dev_get_drvdata(dev); int ret; - unsigned long i; + unsigned int i; - ret = strict_strtoul(buf, 10, &i); + ret = kstrtouint(buf, 10, &i); mutex_lock(&lm->lock); lm->kp_enabled = !i; @@ -635,12 +624,13 @@ static ssize_t lm8323_set_disable(struct device *dev, } static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable); -static int __devinit lm8323_probe(struct i2c_client *client, +static int lm8323_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lm8323_platform_data *pdata = client->dev.platform_data; + struct lm8323_platform_data *pdata = dev_get_platdata(&client->dev); struct input_dev *idev; struct lm8323_chip *lm; + int pwm; int i, err; unsigned long tmo; u8 data[2]; @@ -669,12 +659,9 @@ static int __devinit lm8323_probe(struct i2c_client *client, goto fail1; } - i2c_set_clientdata(client, lm); - lm->client = client; lm->idev = idev; mutex_init(&lm->lock); - INIT_WORK(&lm->work, lm8323_work); lm->size_x = pdata->size_x; lm->size_y = pdata->size_y; @@ -711,8 +698,9 @@ static int __devinit lm8323_probe(struct i2c_client *client, goto fail1; } - for (i = 0; i < LM8323_NUM_PWMS; i++) { - err = init_pwm(lm, i + 1, &client->dev, pdata->pwm_names[i]); + 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; } @@ -744,14 +732,15 @@ static int __devinit lm8323_probe(struct i2c_client *client, goto fail3; } - err = request_irq(client->irq, lm8323_irq, - IRQF_TRIGGER_FALLING | IRQF_DISABLED, - "lm8323", lm); + 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); @@ -763,48 +752,53 @@ fail4: fail3: device_remove_file(&client->dev, &dev_attr_disable_kp); fail2: - while (--i >= 0) - if (lm->pwm[i].enabled) - led_classdev_unregister(&lm->pwm[i].cdev); + 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 __devexit lm8323_remove(struct i2c_client *client) +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); - cancel_work_sync(&lm->work); 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) + 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 +#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 i2c_client *client, pm_message_t mesg) +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; - set_irq_wake(client->irq, 0); + irq_set_irq_wake(client->irq, 0); disable_irq(client->irq); mutex_lock(&lm->lock); @@ -818,8 +812,9 @@ static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int lm8323_resume(struct i2c_client *client) +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; @@ -832,15 +827,14 @@ static int lm8323_resume(struct i2c_client *client) led_classdev_resume(&lm->pwm[i].cdev); enable_irq(client->irq); - set_irq_wake(client->irq, 1); + irq_set_irq_wake(client->irq, 1); return 0; } -#else -#define lm8323_suspend NULL -#define lm8323_resume NULL #endif +static SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume); + static const struct i2c_device_id lm8323_id[] = { { "lm8323", 0 }, { } @@ -849,26 +843,15 @@ static const struct i2c_device_id lm8323_id[] = { static struct i2c_driver lm8323_i2c_driver = { .driver = { .name = "lm8323", + .pm = &lm8323_pm_ops, }, .probe = lm8323_probe, - .remove = __devexit_p(lm8323_remove), - .suspend = lm8323_suspend, - .resume = lm8323_resume, + .remove = lm8323_remove, .id_table = lm8323_id, }; MODULE_DEVICE_TABLE(i2c, lm8323_id); -static int __init lm8323_init(void) -{ - return i2c_add_driver(&lm8323_i2c_driver); -} -module_init(lm8323_init); - -static void __exit lm8323_exit(void) -{ - i2c_del_driver(&lm8323_i2c_driver); -} -module_exit(lm8323_exit); +module_i2c_driver(lm8323_i2c_driver); MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>"); MODULE_AUTHOR("Daniel Stone"); diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c new file mode 100644 index 00000000000..9081cbef11e --- /dev/null +++ b/drivers/input/keyboard/lm8333.c @@ -0,0 +1,236 @@ +/* + * 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 = + dev_get_platdata(&client->dev); + 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"); diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c index 9caed30f3bb..c94d610b9d7 100644 --- a/drivers/input/keyboard/locomokbd.c +++ b/drivers/input/keyboard/locomokbd.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); #define KEY_CENTER KEY_F15 static const unsigned char -locomokbd_keycode[LOCOMOKBD_NUMKEYS] __devinitconst = { +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 */ @@ -192,11 +192,18 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd) 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; } @@ -210,7 +217,26 @@ static void locomokbd_timer_callback(unsigned long data) locomokbd_scankeyboard(locomokbd); } -static int __devinit locomokbd_probe(struct locomo_dev *dev) +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; @@ -253,6 +279,8 @@ static int __devinit locomokbd_probe(struct locomo_dev *dev) 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) | @@ -261,6 +289,8 @@ static int __devinit locomokbd_probe(struct locomo_dev *dev) 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); @@ -291,7 +321,7 @@ static int __devinit locomokbd_probe(struct locomo_dev *dev) return err; } -static int __devexit locomokbd_remove(struct locomo_dev *dev) +static int locomokbd_remove(struct locomo_dev *dev) { struct locomokbd *locomokbd = locomo_get_drvdata(dev); @@ -315,7 +345,7 @@ static struct locomo_driver keyboard_driver = { }, .devid = LOCOMO_DEVID_KEYBOARD, .probe = locomokbd_probe, - .remove = __devexit_p(locomokbd_remove), + .remove = locomokbd_remove, }; static int __init locomokbd_init(void) diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c new file mode 100644 index 00000000000..8b1b01361ec --- /dev/null +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -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 = 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"); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index d3c8b61a941..8d2e19e81e1 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -14,7 +14,6 @@ #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> @@ -22,11 +21,14 @@ #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 short *keycodes; unsigned int row_shift; DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); @@ -36,6 +38,7 @@ struct matrix_keypad { spinlock_t lock; bool scan_pending; bool stopped; + bool gpio_all_disabled; }; /* @@ -86,8 +89,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad) const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; - for (i = 0; i < pdata->num_row_gpios; i++) - enable_irq(gpio_to_irq(pdata->row_gpios[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) @@ -95,8 +102,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad) const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; - for (i = 0; i < pdata->num_row_gpios; i++) - disable_irq_nosync(gpio_to_irq(pdata->row_gpios[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])); + } } /* @@ -107,6 +118,7 @@ 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; @@ -142,7 +154,7 @@ static void matrix_keypad_scan(struct work_struct *work) code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); input_event(input_dev, EV_MSC, MSC_SCAN, code); input_report_key(input_dev, - keypad->keycodes[code], + keycodes[code], new_state[col] & (1 << row)); } } @@ -214,61 +226,85 @@ static void matrix_keypad_stop(struct input_dev *dev) disable_row_irqs(keypad); } -#ifdef CONFIG_PM -static int matrix_keypad_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) { - struct platform_device *pdev = to_platform_device(dev); - struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; + unsigned int gpio; int i; - matrix_keypad_stop(keypad->input_dev); + if (pdata->clustered_irq > 0) { + if (enable_irq_wake(pdata->clustered_irq) == 0) + keypad->gpio_all_disabled = true; + } else { - if (device_may_wakeup(&pdev->dev)) { for (i = 0; i < pdata->num_row_gpios; i++) { if (!test_bit(i, keypad->disabled_gpios)) { - unsigned int gpio = pdata->row_gpios[i]; + gpio = pdata->row_gpios[i]; if (enable_irq_wake(gpio_to_irq(gpio)) == 0) __set_bit(i, keypad->disabled_gpios); } } } - - return 0; } -static int matrix_keypad_resume(struct device *dev) +static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) { - struct platform_device *pdev = to_platform_device(dev); - struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; + unsigned int gpio; int i; - if (device_may_wakeup(&pdev->dev)) { + 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)) { - unsigned int gpio = pdata->row_gpios[i]; - + gpio = pdata->row_gpios[i]; disable_irq_wake(gpio_to_irq(gpio)); } } } +} - matrix_keypad_start(keypad->input_dev); +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 const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, - matrix_keypad_suspend, matrix_keypad_resume); +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 int __devinit init_matrix_gpio(struct platform_device *pdev, - struct matrix_keypad *keypad) +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 = -EINVAL; + int i, err; /* initialized strobe lines as outputs, activated */ for (i = 0; i < pdata->num_col_gpios; i++) { @@ -295,17 +331,29 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev, gpio_direction_input(pdata->row_gpios[i]); } - for (i = 0; i < pdata->num_row_gpios; i++) { - err = request_irq(gpio_to_irq(pdata->row_gpios[i]), + if (pdata->clustered_irq > 0) { + err = request_irq(pdata->clustered_irq, matrix_keypad_interrupt, - IRQF_DISABLED | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + pdata->clustered_irq_flags, "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; + "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; + } } } @@ -328,44 +376,123 @@ err_free_cols: return err; } -static int __devinit matrix_keypad_probe(struct platform_device *pdev) +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; - const struct matrix_keymap_data *keymap_data; struct matrix_keypad *keypad; struct input_dev *input_dev; - unsigned short *keycodes; - unsigned int row_shift; int err; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - keymap_data = pdata->keymap_data; - if (!keymap_data) { + 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; } - row_shift = get_count_order(pdata->num_col_gpios); - keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); - keycodes = kzalloc((pdata->num_row_gpios << row_shift) * - sizeof(*keycodes), - GFP_KERNEL); input_dev = input_allocate_device(); - if (!keypad || !keycodes || !input_dev) { + if (!keypad || !input_dev) { err = -ENOMEM; goto err_free_mem; } keypad->input_dev = input_dev; keypad->pdata = pdata; - keypad->keycodes = keycodes; - keypad->row_shift = row_shift; + 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); @@ -373,88 +500,76 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_dev->open = matrix_keypad_start; input_dev->close = matrix_keypad_stop; - input_dev->keycode = keycodes; - input_dev->keycodesize = sizeof(*keycodes); - input_dev->keycodemax = pdata->num_row_gpios << row_shift; - - matrix_keypad_build_keymap(keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); + 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 = init_matrix_gpio(pdev, 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_mem; + 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(keycodes); kfree(keypad); return err; } -static int __devexit matrix_keypad_remove(struct platform_device *pdev) +static int matrix_keypad_remove(struct platform_device *pdev) { struct matrix_keypad *keypad = platform_get_drvdata(pdev); - const struct matrix_keypad_platform_data *pdata = keypad->pdata; - int i; device_init_wakeup(&pdev->dev, 0); - for (i = 0; i < pdata->num_row_gpios; i++) { - free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); - gpio_free(pdata->row_gpios[i]); - } - - for (i = 0; i < pdata->num_col_gpios; i++) - gpio_free(pdata->col_gpios[i]); - + matrix_keypad_free_gpio(keypad); input_unregister_device(keypad->input_dev); - platform_set_drvdata(pdev, NULL); - kfree(keypad->keycodes); kfree(keypad); 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 = __devexit_p(matrix_keypad_remove), + .remove = matrix_keypad_remove, .driver = { .name = "matrix-keypad", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &matrix_keypad_pm_ops, -#endif + .of_match_table = of_match_ptr(matrix_keypad_dt_match), }, }; - -static int __init matrix_keypad_init(void) -{ - return platform_driver_register(&matrix_keypad_driver); -} - -static void __exit matrix_keypad_exit(void) -{ - platform_driver_unregister(&matrix_keypad_driver); -} - -module_init(matrix_keypad_init); -module_exit(matrix_keypad_exit); +module_platform_driver(matrix_keypad_driver); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 3b5b948eba3..430b5453972 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -15,7 +15,9 @@ #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> @@ -177,10 +179,11 @@ static void max7359_initialize(struct i2c_client *client) max7359_fall_deepsleep(client); } -static int __devinit max7359_probe(struct i2c_client *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; + const struct matrix_keymap_data *keymap_data = + dev_get_platdata(&client->dev); struct max7359_keypad *keypad; struct input_dev *input_dev; int ret; @@ -258,21 +261,22 @@ failed_free_mem: return error; } -static int __devexit max7359_remove(struct i2c_client *client) +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); - i2c_set_clientdata(client, NULL); kfree(keypad); return 0; } -#ifdef CONFIG_PM -static int max7359_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +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)) @@ -281,8 +285,10 @@ static int max7359_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int max7359_resume(struct i2c_client *client) +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); @@ -291,11 +297,10 @@ static int max7359_resume(struct i2c_client *client) return 0; } -#else -#define max7359_suspend NULL -#define max7359_resume NULL #endif +static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume); + static const struct i2c_device_id max7359_ids[] = { { "max7359", 0 }, { } @@ -305,25 +310,14 @@ MODULE_DEVICE_TABLE(i2c, max7359_ids); static struct i2c_driver max7359_i2c_driver = { .driver = { .name = "max7359", + .pm = &max7359_pm, }, .probe = max7359_probe, - .remove = __devexit_p(max7359_remove), - .suspend = max7359_suspend, - .resume = max7359_resume, + .remove = max7359_remove, .id_table = max7359_ids, }; -static int __init max7359_init(void) -{ - return i2c_add_driver(&max7359_i2c_driver); -} -module_init(max7359_init); - -static void __exit max7359_exit(void) -{ - i2c_del_driver(&max7359_i2c_driver); -} -module_exit(max7359_exit); +module_i2c_driver(max7359_i2c_driver); MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver"); diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c new file mode 100644 index 00000000000..375b05ca8e2 --- /dev/null +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -0,0 +1,283 @@ +/* + * 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/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 = dev_get_platdata(&client->dev); + 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 = "MELFAS 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"); diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c new file mode 100644 index 00000000000..009c82256e8 --- /dev/null +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -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/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 = + dev_get_platdata(&client->dev); + 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"); diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c index 48d1cab0aa1..20f04437799 100644 --- a/drivers/input/keyboard/newtonkbd.c +++ b/drivers/input/keyboard/newtonkbd.c @@ -29,7 +29,6 @@ #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" @@ -166,15 +165,4 @@ static struct serio_driver nkbd_drv = { .disconnect = nkbd_disconnect, }; -static int __init nkbd_init(void) -{ - return serio_register_driver(&nkbd_drv); -} - -static void __exit nkbd_exit(void) -{ - serio_unregister_driver(&nkbd_drv); -} - -module_init(nkbd_init); -module_exit(nkbd_exit); +module_serio_driver(nkbd_drv); diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c new file mode 100644 index 00000000000..63332e2f862 --- /dev/null +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -0,0 +1,439 @@ +/* + * 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 = + dev_get_platdata(&pdev->dev); + 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"); diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c new file mode 100644 index 00000000000..b31064981e9 --- /dev/null +++ b/drivers/input/keyboard/nspire-keypad.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> + * + * 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/input/matrix_keypad.h> +#include <linux/platform_device.h> +#include <linux/interrupt.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/of.h> + +#define KEYPAD_SCAN_MODE 0x00 +#define KEYPAD_CNTL 0x04 +#define KEYPAD_INT 0x08 +#define KEYPAD_INTMSK 0x0C + +#define KEYPAD_DATA 0x10 +#define KEYPAD_GPIO 0x30 + +#define KEYPAD_UNKNOWN_INT 0x40 +#define KEYPAD_UNKNOWN_INT_STS 0x44 + +#define KEYPAD_BITMASK_COLS 11 +#define KEYPAD_BITMASK_ROWS 8 + +struct nspire_keypad { + void __iomem *reg_base; + u32 int_mask; + + struct input_dev *input; + struct clk *clk; + + struct matrix_keymap_data *keymap; + int row_shift; + + /* Maximum delay estimated assuming 33MHz APB */ + u32 scan_interval; /* In microseconds (~2000us max) */ + u32 row_delay; /* In microseconds (~500us max) */ + + u16 state[KEYPAD_BITMASK_ROWS]; + + bool active_low; +}; + +static irqreturn_t nspire_keypad_irq(int irq, void *dev_id) +{ + struct nspire_keypad *keypad = dev_id; + struct input_dev *input = keypad->input; + unsigned short *keymap = input->keycode; + unsigned int code; + int row, col; + u32 int_sts; + u16 state[8]; + u16 bits, changed; + + int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask; + if (!int_sts) + return IRQ_NONE; + + memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state)); + + for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) { + bits = state[row]; + if (keypad->active_low) + bits = ~bits; + + changed = bits ^ keypad->state[row]; + if (!changed) + continue; + + keypad->state[row] = bits; + + for (col = 0; col < KEYPAD_BITMASK_COLS; col++) { + if (!(changed & (1U << col))) + continue; + + code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keymap[code], + bits & (1U << col)); + } + } + + input_sync(input); + + writel(0x3, keypad->reg_base + KEYPAD_INT); + + return IRQ_HANDLED; +} + +static int nspire_keypad_chip_init(struct nspire_keypad *keypad) +{ + unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles; + + cycles_per_us = (clk_get_rate(keypad->clk) / 1000000); + if (cycles_per_us == 0) + cycles_per_us = 1; + + delay_cycles = cycles_per_us * keypad->scan_interval; + WARN_ON(delay_cycles >= (1 << 16)); /* Overflow */ + delay_cycles &= 0xffff; + + row_delay_cycles = cycles_per_us * keypad->row_delay; + WARN_ON(row_delay_cycles >= (1 << 14)); /* Overflow */ + row_delay_cycles &= 0x3fff; + + val |= 3 << 0; /* Set scan mode to 3 (continuous scan) */ + val |= row_delay_cycles << 2; /* Delay between scanning each row */ + val |= delay_cycles << 16; /* Delay between scans */ + writel(val, keypad->reg_base + KEYPAD_SCAN_MODE); + + val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8; + writel(val, keypad->reg_base + KEYPAD_CNTL); + + /* Enable interrupts */ + keypad->int_mask = 1 << 1; + writel(keypad->int_mask, keypad->reg_base + KEYPAD_INTMSK); + + /* Disable GPIO interrupts to prevent hanging on touchpad */ + /* Possibly used to detect touchpad events */ + writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); + /* Acknowledge existing interrupts */ + writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); + + return 0; +} + +static int nspire_keypad_open(struct input_dev *input) +{ + struct nspire_keypad *keypad = input_get_drvdata(input); + int error; + + error = clk_prepare_enable(keypad->clk); + if (error) + return error; + + error = nspire_keypad_chip_init(keypad); + if (error) { + clk_disable_unprepare(keypad->clk); + return error; + } + + return 0; +} + +static void nspire_keypad_close(struct input_dev *input) +{ + struct nspire_keypad *keypad = input_get_drvdata(input); + + clk_disable_unprepare(keypad->clk); +} + +static int nspire_keypad_probe(struct platform_device *pdev) +{ + const struct device_node *of_node = pdev->dev.of_node; + struct nspire_keypad *keypad; + struct input_dev *input; + struct resource *res; + int irq; + int error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -EINVAL; + } + + keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad), + GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + return -ENOMEM; + } + + keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS); + + error = of_property_read_u32(of_node, "scan-interval", + &keypad->scan_interval); + if (error) { + dev_err(&pdev->dev, "failed to get scan-interval\n"); + return error; + } + + error = of_property_read_u32(of_node, "row-delay", + &keypad->row_delay); + if (error) { + dev_err(&pdev->dev, "failed to get row-delay\n"); + return error; + } + + keypad->active_low = of_property_read_bool(of_node, "active-low"); + + keypad->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "unable to get clock\n"); + return PTR_ERR(keypad->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + keypad->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(keypad->reg_base)) + return PTR_ERR(keypad->reg_base); + + keypad->input = input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + input_set_drvdata(input, keypad); + + input->id.bustype = BUS_HOST; + input->name = "nspire-keypad"; + input->open = nspire_keypad_open; + input->close = nspire_keypad_close; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_REP, input->evbit); + input_set_capability(input, EV_MSC, MSC_SCAN); + + error = matrix_keypad_build_keymap(NULL, NULL, + KEYPAD_BITMASK_ROWS, + KEYPAD_BITMASK_COLS, + NULL, input); + if (error) { + dev_err(&pdev->dev, "building keymap failed\n"); + return error; + } + + error = devm_request_irq(&pdev->dev, irq, nspire_keypad_irq, 0, + "nspire_keypad", keypad); + if (error) { + dev_err(&pdev->dev, "allocate irq %d failed\n", irq); + 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); + + dev_dbg(&pdev->dev, + "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n", + res, keypad->row_delay, keypad->scan_interval, + keypad->active_low ? ", active_low" : ""); + + return 0; +} + +static const struct of_device_id nspire_keypad_dt_match[] = { + { .compatible = "ti,nspire-keypad" }, + { }, +}; +MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match); + +static struct platform_driver nspire_keypad_driver = { + .driver = { + .name = "nspire-keypad", + .owner = THIS_MODULE, + .of_match_table = nspire_keypad_dt_match, + }, + .probe = nspire_keypad_probe, +}; + +module_platform_driver(nspire_keypad_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver"); diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 1a494d50543..b1acc9852eb 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -25,7 +25,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/types.h> #include <linux/input.h> @@ -34,13 +33,10 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/errno.h> -#include <mach/gpio.h> -#include <plat/keypad.h> -#include <plat/menelaus.h> -#include <asm/irq.h> -#include <mach/hardware.h> -#include <asm/io.h> -#include <plat/mux.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 @@ -60,11 +56,11 @@ struct omap_kp { unsigned int cols; unsigned long delay; unsigned int debounce; + unsigned short keymap[]; }; static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); -static int *keymap; static unsigned int *row_gpios; static unsigned int *col_gpios; @@ -95,28 +91,8 @@ static u8 get_row_gpio_val(struct omap_kp *omap_kp) static irqreturn_t omap_kp_interrupt(int irq, void *dev_id) { - struct omap_kp *omap_kp = dev_id; - /* disable keyboard interrupt and schedule for handling */ - if (cpu_is_omap24xx()) { - int i; - - for (i = 0; i < omap_kp->rows; i++) { - int gpio_irq = gpio_to_irq(row_gpios[i]); - /* - * The interrupt which we're currently handling should - * be disabled _nosync() to avoid deadlocks waiting - * for this handler to complete. All others should - * be disabled the regular way for SMP safety. - */ - if (gpio_irq == irq) - disable_irq_nosync(gpio_irq); - else - disable_irq(gpio_irq); - } - } else - /* disable keyboard interrupt and schedule for handling */ - omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); tasklet_schedule(&kp_tasklet); @@ -132,49 +108,29 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) { int col = 0; - /* read the keypad status */ - if (cpu_is_omap24xx()) { - /* read the keypad status */ - for (col = 0; col < omap_kp->cols; col++) { - set_col_gpio_val(omap_kp, ~(1 << col)); - state[col] = ~(get_row_gpio_val(omap_kp)) & 0xff; - } - set_col_gpio_val(omap_kp, 0); - - } else { - /* disable keyboard interrupt and schedule for handling */ - omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + /* 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); + /* 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); + 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); + state[col] = ~omap_readw(OMAP1_MPUIO_BASE + + OMAP_MPUIO_KBR_LATCH) & 0xff; } -} - -static inline int omap_kp_find_key(int col, int row) -{ - int i, key; - - key = KEY(col, row, 0); - for (i = 0; keymap[i] != 0; i++) - if ((keymap[i] & 0xff000000) == key) - return keymap[i] & 0x00ffffff; - return -1; + 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; @@ -198,7 +154,7 @@ static void omap_kp_tasklet(unsigned long data) row, (new_state[col] & (1 << row)) ? "pressed" : "released"); #else - key = omap_kp_find_key(col, row); + key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; if (key < 0) { printk(KERN_WARNING "omap-keypad: Spurious key event %d-%d\n", @@ -218,10 +174,11 @@ static void omap_kp_tasklet(unsigned long data) #endif } } + input_sync(omap_kp_data->input); memcpy(keypad_state, new_state, sizeof(keypad_state)); if (key_down) { - int delay = HZ / 20; + int delay = HZ / 20; /* some key is pressed - keep irq disabled and use timer * to poll the keypad */ if (spurious) @@ -229,14 +186,8 @@ static void omap_kp_tasklet(unsigned long data) mod_timer(&omap_kp_data->timer, jiffies + delay); } else { /* enable interrupts */ - if (cpu_is_omap24xx()) { - int i; - for (i = 0; i < omap_kp_data->rows; i++) - enable_irq(gpio_to_irq(row_gpios[i])); - } else { - omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - kp_cur_group = -1; - } + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + kp_cur_group = -1; } } @@ -249,6 +200,7 @@ static ssize_t omap_kp_enable_show(struct device *dev, 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) @@ -260,9 +212,9 @@ static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute mutex_lock(&kp_enable_mutex); if (state != kp_enable) { if (state) - enable_irq(INT_KEYBOARD); + enable_irq(omap_kp->irq); else - disable_irq(INT_KEYBOARD); + disable_irq(omap_kp->irq); kp_enable = state; } mutex_unlock(&kp_enable_mutex); @@ -291,19 +243,24 @@ static int omap_kp_resume(struct platform_device *dev) #define omap_kp_resume NULL #endif -static int __devinit omap_kp_probe(struct platform_device *pdev) +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, irq_idx, ret; + struct omap_kp_platform_data *pdata = dev_get_platdata(&pdev->dev); + int i, col_idx, row_idx, ret; + unsigned int row_shift, keycodemax; - if (!pdata->rows || !pdata->cols || !pdata->keymap) { - printk(KERN_ERR "No rows, cols or keymap from pdata\n"); + if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { + printk(KERN_ERR "No rows, cols or keymap_data from pdata\n"); return -EINVAL; } - omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL); + 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); @@ -316,13 +273,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) omap_kp->input = input_dev; /* Disable the interrupt for the MPUIO keyboard */ - if (!cpu_is_omap24xx()) - omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - - keymap = pdata->keymap; - - if (pdata->rep) - __set_bit(EV_REP, input_dev->evbit); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); if (pdata->delay) omap_kp->delay = pdata->delay; @@ -335,31 +286,8 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) omap_kp->rows = pdata->rows; omap_kp->cols = pdata->cols; - if (cpu_is_omap24xx()) { - /* Cols: outputs */ - for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) { - if (gpio_request(col_gpios[col_idx], "omap_kp_col") < 0) { - printk(KERN_ERR "Failed to request" - "GPIO%d for keypad\n", - col_gpios[col_idx]); - goto err1; - } - gpio_direction_output(col_gpios[col_idx], 0); - } - /* Rows: inputs */ - for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) { - if (gpio_request(row_gpios[row_idx], "omap_kp_row") < 0) { - printk(KERN_ERR "Failed to request" - "GPIO%d for keypad\n", - row_gpios[row_idx]); - goto err2; - } - gpio_direction_input(row_gpios[row_idx]); - } - } else { - col_idx = 0; - row_idx = 0; - } + col_idx = 0; + row_idx = 0; setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); @@ -372,9 +300,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) goto err2; /* setup input device */ - __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; keymap[i] != 0; i++) - __set_bit(keymap[i] & KEY_MAX, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; input_dev->dev.parent = &pdev->dev; @@ -384,6 +309,15 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) 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"); @@ -395,37 +329,25 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) /* scan current status and enable interrupt */ omap_kp_scan_keypad(omap_kp, keypad_state); - if (!cpu_is_omap24xx()) { - 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); - } else { - for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { - if (request_irq(gpio_to_irq(row_gpios[irq_idx]), - omap_kp_interrupt, - IRQF_TRIGGER_FALLING, - "omap-keypad", omap_kp) < 0) - goto err5; - } + 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; -err5: - for (i = irq_idx - 1; i >=0; i--) - free_irq(row_gpios[i], 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--) + for (i = row_idx - 1; i >= 0; i--) gpio_free(row_gpios[i]); -err1: - for (i = col_idx - 1; i >=0; i--) + for (i = col_idx - 1; i >= 0; i--) gpio_free(col_gpios[i]); kfree(omap_kp); @@ -434,24 +356,14 @@ err1: return -EINVAL; } -static int __devexit omap_kp_remove(struct platform_device *pdev) +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); - if (cpu_is_omap24xx()) { - int i; - for (i = 0; i < omap_kp->cols; i++) - gpio_free(col_gpios[i]); - for (i = 0; i < omap_kp->rows; i++) { - gpio_free(row_gpios[i]); - free_irq(gpio_to_irq(row_gpios[i]), 0); - } - } else { - omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - free_irq(omap_kp->irq, 0); - } + 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); @@ -466,7 +378,7 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) static struct platform_driver omap_kp_driver = { .probe = omap_kp_probe, - .remove = __devexit_p(omap_kp_remove), + .remove = omap_kp_remove, .suspend = omap_kp_suspend, .resume = omap_kp_resume, .driver = { @@ -474,20 +386,7 @@ static struct platform_driver omap_kp_driver = { .owner = THIS_MODULE, }, }; - -static int __init omap_kp_init(void) -{ - printk(KERN_INFO "OMAP Keypad Driver\n"); - return platform_driver_register(&omap_kp_driver); -} - -static void __exit omap_kp_exit(void) -{ - platform_driver_unregister(&omap_kp_driver); -} - -module_init(omap_kp_init); -module_exit(omap_kp_exit); +module_platform_driver(omap_kp_driver); MODULE_AUTHOR("Timo Teräs"); MODULE_DESCRIPTION("OMAP Keypad Driver"); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c new file mode 100644 index 00000000000..024b7bdffe5 --- /dev/null +++ b/drivers/input/keyboard/omap4-keypad.c @@ -0,0 +1,473 @@ +/* + * 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/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/input/matrix_keypad.h> +#include <linux/slab.h> +#include <linux/pm_runtime.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 BIT(0) +#define OMAP4_DEF_IRQENABLE_LONGKEY BIT(1) +#define OMAP4_DEF_WUP_EVENT_ENA BIT(0) +#define OMAP4_DEF_WUP_LONG_KEY_ENA BIT(1) +#define OMAP4_DEF_CTRL_NOSOFTMODE BIT(1) +#define OMAP4_DEF_CTRL_PTV_SHIFT 2 + +/* OMAP4 values */ +#define OMAP4_VAL_IRQDISABLE 0x0 +#define OMAP4_VAL_DEBOUNCINGTIME 0x7 +#define OMAP4_VAL_PVT 0x7 + +enum { + KBD_REVISION_OMAP4 = 0, + KBD_REVISION_OMAP5, +}; + +struct omap4_keypad { + struct input_dev *input; + + void __iomem *base; + bool irq_wake_enabled; + 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 handlers */ +static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) +{ + struct omap4_keypad *keypad_data = dev_id; + + if (kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)) { + /* Disable interrupts */ + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, + OMAP4_VAL_IRQDISABLE); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t omap4_keypad_irq_thread_fn(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; + + *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_DEF_CTRL_NOSOFTMODE | + (OMAP4_VAL_PVT << OMAP4_DEF_CTRL_PTV_SHIFT)); + kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME, + OMAP4_VAL_DEBOUNCINGTIME); + /* clear pending interrupts */ + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, + kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); + 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); +} + +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; +} + +static int omap4_keypad_probe(struct platform_device *pdev) +{ + 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; + + 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(NULL, 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_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler, + omap4_keypad_irq_thread_fn, 0, + "omap4-keypad", keypad_data); + if (error) { + dev_err(&pdev->dev, "failed to register interrupt\n"); + goto err_free_input; + } + + device_init_wakeup(&pdev->dev, true); + 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); + device_init_wakeup(&pdev->dev, false); + 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); + + device_init_wakeup(&pdev->dev, false); + + 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); + + return 0; +} + +static const struct of_device_id omap_keypad_dt_match[] = { + { .compatible = "ti,omap4-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_keypad_dt_match); + +#ifdef CONFIG_PM_SLEEP +static int omap4_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + int error; + + if (device_may_wakeup(&pdev->dev)) { + error = enable_irq_wake(keypad_data->irq); + if (!error) + keypad_data->irq_wake_enabled = true; + } + + return 0; +} + +static int omap4_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) { + disable_irq_wake(keypad_data->irq); + keypad_data->irq_wake_enabled = false; + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops, + omap4_keypad_suspend, omap4_keypad_resume); + +static struct platform_driver omap4_keypad_driver = { + .probe = omap4_keypad_probe, + .remove = omap4_keypad_remove, + .driver = { + .name = "omap4-keypad", + .owner = THIS_MODULE, + .pm = &omap4_keypad_pm_ops, + .of_match_table = 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"); diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index 78cccddbf55..7b9b44158ad 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/slab.h> struct opencores_kbd { struct input_dev *input; @@ -36,7 +37,7 @@ static irqreturn_t opencores_kbd_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit opencores_kbd_probe(struct platform_device *pdev) +static int opencores_kbd_probe(struct platform_device *pdev) { struct input_dev *input; struct opencores_kbd *opencores_kbd; @@ -138,7 +139,7 @@ static int __devinit opencores_kbd_probe(struct platform_device *pdev) return error; } -static int __devexit opencores_kbd_remove(struct platform_device *pdev) +static int opencores_kbd_remove(struct platform_device *pdev) { struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev); @@ -150,30 +151,17 @@ static int __devexit opencores_kbd_remove(struct platform_device *pdev) 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 = __devexit_p(opencores_kbd_remove), + .remove = opencores_kbd_remove, .driver = { .name = "opencores-kbd", }, }; - -static int __init opencores_kbd_init(void) -{ - return platform_driver_register(&opencores_kbd_device_driver); -} -module_init(opencores_kbd_init); - -static void __exit opencores_kbd_exit(void) -{ - platform_driver_unregister(&opencores_kbd_device_driver); -} -module_exit(opencores_kbd_exit); +module_platform_driver(opencores_kbd_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>"); diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c new file mode 100644 index 00000000000..80c6b0ef3fc --- /dev/null +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -0,0 +1,701 @@ +/* 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/regmap.h> +#include <linux/of.h> +#include <linux/input/matrix_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 + * @num_cols - number of columns of keypad + * @num_rows - number of row of keypad + * @input - input device pointer for keypad + * @regmap - regmap handle + * @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 { + unsigned int num_rows; + unsigned int num_cols; + struct input_dev *input; + struct regmap *regmap; + 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 u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) +{ + /* all keys pressed on that particular row? */ + if (col == 0x00) + return 1 << kp->num_cols; + else + return col & ((1 << kp->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; + unsigned int scan_val; + + rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val |= 0x1; + + rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val); + 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; + unsigned int val; + + for (row = 0; row < read_rows; row++) { + rc = regmap_read(kp->regmap, data_reg, &val); + if (rc) + return rc; + dev_dbg(kp->dev, "%d = %d\n", row, val); + state[row] = pmic8xxx_col_state(kp, val); + } + + return 0; +} + +static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int rc, read_rows; + unsigned int scan_val; + + if (kp->num_rows < PM8XXX_MIN_ROWS) + read_rows = PM8XXX_MIN_ROWS; + else + read_rows = kp->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 = regmap_read(kp->regmap, KEYP_SCAN, &scan_val); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val &= 0xFE; + rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val); + 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->num_rows; row++) { + int bits_changed = new_state[row] ^ old_state[row]; + + if (!bits_changed) + continue; + + for (col = 0; col < kp->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->num_rows; row++) { + row_state = (~new_state[row]) & + ((1 << kp->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; + unsigned int ctrl_val, events; + int rc; + + rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val); + 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, + struct platform_device *pdev) +{ + const struct device_node *of_node = pdev->dev.of_node; + unsigned int scan_delay_ms; + unsigned int row_hold_ns; + unsigned int debounce_ms; + 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->num_cols < KEYP_CTRL_SCAN_COLS_MIN) + bits = 0; + else + bits = kp->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->num_rows < KEYP_CTRL_SCAN_ROWS_MIN) + bits = 0; + else + bits = row_bits[kp->num_rows - KEYP_CTRL_SCAN_ROWS_MIN]; + + ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT); + + rc = regmap_write(kp->regmap, KEYP_CTRL, ctrl_val); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + return rc; + } + + if (of_property_read_u32(of_node, "scan-delay", &scan_delay_ms)) + scan_delay_ms = MIN_SCAN_DELAY; + + if (scan_delay_ms > MAX_SCAN_DELAY || scan_delay_ms < MIN_SCAN_DELAY || + !is_power_of_2(scan_delay_ms)) { + dev_err(&pdev->dev, "invalid keypad scan time supplied\n"); + return -EINVAL; + } + + if (of_property_read_u32(of_node, "row-hold", &row_hold_ns)) + row_hold_ns = MIN_ROW_HOLD_DELAY; + + if (row_hold_ns > MAX_ROW_HOLD_DELAY || + row_hold_ns < MIN_ROW_HOLD_DELAY || + ((row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) { + dev_err(&pdev->dev, "invalid keypad row hold time supplied\n"); + return -EINVAL; + } + + if (of_property_read_u32(of_node, "debounce", &debounce_ms)) + debounce_ms = MIN_DEBOUNCE_TIME; + + if (((debounce_ms % 5) != 0) || + debounce_ms > MAX_DEBOUNCE_TIME || + debounce_ms < MIN_DEBOUNCE_TIME) { + dev_err(&pdev->dev, "invalid debounce time supplied\n"); + return -EINVAL; + } + + bits = (debounce_ms / 5) - 1; + + scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT); + + bits = fls(scan_delay_ms) - 1; + scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT); + + /* Row hold time is a multiple of 32KHz cycles. */ + cycles = (row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC; + + scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT); + + rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val); + if (rc) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; + +} + +static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg |= KEYP_CTRL_KEYP_EN; + + rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); + 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 = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); + 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) +{ + unsigned int rows, cols; + bool repeat; + bool wakeup; + struct pmic8xxx_kp *kp; + int rc; + unsigned int ctrl_val; + + rc = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols); + if (rc) + return rc; + + if (cols > PM8XXX_MAX_COLS || rows > PM8XXX_MAX_ROWS || + cols < PM8XXX_MIN_COLS) { + dev_err(&pdev->dev, "invalid platform data\n"); + return -EINVAL; + } + + repeat = !of_property_read_bool(pdev->dev.of_node, + "linux,input-no-autorepeat"); + wakeup = of_property_read_bool(pdev->dev.of_node, + "linux,keypad-wakeup"); + + kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + kp->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!kp->regmap) + return -ENODEV; + + platform_set_drvdata(pdev, kp); + + kp->num_rows = rows; + kp->num_cols = cols; + kp->dev = &pdev->dev; + + kp->input = devm_input_allocate_device(&pdev->dev); + if (!kp->input) { + dev_err(&pdev->dev, "unable to allocate input device\n"); + return -ENOMEM; + } + + 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"); + return kp->key_sense_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"); + return kp->key_stuck_irq; + } + + kp->input->name = "PMIC8XXX keypad"; + kp->input->phys = "pmic8xxx_keypad/input0"; + + 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(NULL, NULL, + PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS, + kp->keycodes, kp->input); + if (rc) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return rc; + } + + if (repeat) + __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, pdev); + if (rc < 0) { + dev_err(&pdev->dev, "unable to initialize keypad controller\n"); + return rc; + } + + rc = devm_request_any_context_irq(&pdev->dev, 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"); + return rc; + } + + rc = devm_request_any_context_irq(&pdev->dev, 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"); + return rc; + } + + rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val); + if (rc < 0) { + dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n"); + return rc; + } + + 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"); + return rc; + } + + device_init_wakeup(&pdev->dev, wakeup); + + 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 const struct of_device_id pm8xxx_match_table[] = { + { .compatible = "qcom,pm8058-keypad" }, + { .compatible = "qcom,pm8921-keypad" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_match_table); + +static struct platform_driver pmic8xxx_kp_driver = { + .probe = pmic8xxx_kp_probe, + .driver = { + .name = "pm8xxx-keypad", + .owner = THIS_MODULE, + .pm = &pm8xxx_kp_pm_ops, + .of_match_table = pm8xxx_match_table, + }, +}; +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>"); diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 79cd3e9fdf2..a15063bea70 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/device.h> @@ -26,12 +25,14 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/input/matrix_keypad.h> +#include <linux/slab.h> +#include <linux/of.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <mach/hardware.h> -#include <mach/pxa27x_keypad.h> +#include <linux/platform_data/keypad-pxa27x.h> /* * Keypad Controller registers */ @@ -99,7 +100,7 @@ #define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM) struct pxa27x_keypad { - struct pxa27x_keypad_platform_data *pdata; + const struct pxa27x_keypad_platform_data *pdata; struct clk *clk; struct input_dev *input_dev; @@ -110,6 +111,8 @@ struct pxa27x_keypad { unsigned short keycodes[MAX_KEYPAD_KEYS]; int rotary_rel_code[2]; + unsigned int row_shift; + /* state row bits of each column scan */ uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS]; uint32_t direct_key_state; @@ -117,25 +120,254 @@ struct pxa27x_keypad { unsigned int direct_key_mask; }; -static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) +#ifdef CONFIG_OF +static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, + struct pxa27x_keypad_platform_data *pdata) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; - unsigned short keycode; + struct device *dev = input_dev->dev.parent; + u32 rows, cols; + int error; + + error = matrix_keypad_parse_of_params(dev, &rows, &cols); + if (error) + return error; + + if (rows > MAX_MATRIX_KEY_ROWS || cols > MAX_MATRIX_KEY_COLS) { + dev_err(dev, "rows or cols exceeds maximum value\n"); + return -EINVAL; + } + + pdata->matrix_key_rows = rows; + pdata->matrix_key_cols = cols; + + error = matrix_keypad_build_keymap(NULL, NULL, + pdata->matrix_key_rows, + pdata->matrix_key_cols, + keypad->keycodes, input_dev); + if (error) + return error; + + return 0; +} + +static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad, + struct pxa27x_keypad_platform_data *pdata) +{ + struct input_dev *input_dev = keypad->input_dev; + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + const __be16 *prop; + unsigned short code; + unsigned int proplen, size; int i; + int error; - 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); + error = of_property_read_u32(np, "marvell,direct-key-count", + &pdata->direct_key_num); + if (error) { + /* + * If do not have marvel,direct-key-count defined, + * it means direct key is not supported. + */ + return error == -EINVAL ? 0 : error; + } - keycode = KEY_VAL(key); - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); + error = of_property_read_u32(np, "marvell,direct-key-mask", + &pdata->direct_key_mask); + if (error) { + if (error != -EINVAL) + return error; + + /* + * If marvell,direct-key-mask is not defined, driver will use + * default value. Default value is set when configure the keypad. + */ + pdata->direct_key_mask = 0; + } + + pdata->direct_key_low_active = of_property_read_bool(np, + "marvell,direct-key-low-active"); + + prop = of_get_property(np, "marvell,direct-key-map", &proplen); + if (!prop) + return -EINVAL; + + if (proplen % sizeof(u16)) + return -EINVAL; + + size = proplen / sizeof(u16); + + /* Only MAX_DIRECT_KEY_NUM is accepted.*/ + if (size > MAX_DIRECT_KEY_NUM) + return -EINVAL; + + for (i = 0; i < size; i++) { + code = be16_to_cpup(prop + i); + keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = code; + __set_bit(code, input_dev->keybit); } + return 0; +} + +static int pxa27x_keypad_rotary_parse_dt(struct pxa27x_keypad *keypad, + struct pxa27x_keypad_platform_data *pdata) +{ + const __be32 *prop; + int i, relkey_ret; + unsigned int code, proplen; + const char *rotaryname[2] = { + "marvell,rotary0", "marvell,rotary1"}; + const char relkeyname[] = {"marvell,rotary-rel-key"}; + struct input_dev *input_dev = keypad->input_dev; + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + + relkey_ret = of_property_read_u32(np, relkeyname, &code); + /* if can read correct rotary key-code, we do not need this. */ + if (relkey_ret == 0) { + unsigned short relcode; + + /* rotary0 taks lower half, rotary1 taks upper half. */ + relcode = code & 0xffff; + pdata->rotary0_rel_code = (code & 0xffff); + __set_bit(relcode, input_dev->relbit); + + relcode = code >> 16; + pdata->rotary1_rel_code = relcode; + __set_bit(relcode, input_dev->relbit); + } + + for (i = 0; i < 2; i++) { + prop = of_get_property(np, rotaryname[i], &proplen); + /* + * If the prop is not set, it means keypad does not need + * initialize the rotaryX. + */ + if (!prop) + continue; + + code = be32_to_cpup(prop); + /* + * Not all up/down key code are valid. + * Now we depends on direct-rel-code. + */ + if ((!(code & 0xffff) || !(code >> 16)) && relkey_ret) { + return relkey_ret; + } else { + unsigned int n = MAX_MATRIX_KEY_NUM + (i << 1); + unsigned short keycode; + + keycode = code & 0xffff; + keypad->keycodes[n] = keycode; + __set_bit(keycode, input_dev->keybit); + + keycode = code >> 16; + keypad->keycodes[n + 1] = keycode; + __set_bit(keycode, input_dev->keybit); + + if (i == 0) + pdata->rotary0_rel_code = -1; + else + pdata->rotary1_rel_code = -1; + } + if (i == 0) + pdata->enable_rotary0 = 1; + else + pdata->enable_rotary1 = 1; + } + + keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; + keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + + return 0; +} + +static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) +{ + struct input_dev *input_dev = keypad->input_dev; + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + struct pxa27x_keypad_platform_data *pdata; + int error; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "failed to allocate memory for pdata\n"); + return -ENOMEM; + } + + error = pxa27x_keypad_matrix_key_parse_dt(keypad, pdata); + if (error) { + dev_err(dev, "failed to parse matrix key\n"); + return error; + } + + error = pxa27x_keypad_direct_key_parse_dt(keypad, pdata); + if (error) { + dev_err(dev, "failed to parse direct key\n"); + return error; + } + + error = pxa27x_keypad_rotary_parse_dt(keypad, pdata); + if (error) { + dev_err(dev, "failed to parse rotary key\n"); + return error; + } + + error = of_property_read_u32(np, "marvell,debounce-interval", + &pdata->debounce_interval); + if (error) { + dev_err(dev, "failed to parse debpunce-interval\n"); + return error; + } + + /* + * The keycodes may not only includes matrix key but also the direct + * key or rotary key. + */ + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + keypad->pdata = pdata; + return 0; +} + +#else + +static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) +{ + dev_info(keypad->input_dev->dev.parent, "missing platform data\n"); + + return -EINVAL; +} + +#endif + +static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) +{ + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; + const struct matrix_keymap_data *keymap_data = + pdata ? pdata->matrix_keymap_data : NULL; + unsigned short keycode; + int i; + int error; + + error = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->matrix_key_rows, + pdata->matrix_key_cols, + keypad->keycodes, input_dev); + if (error) + return error; + + /* + * The keycodes may not only include matrix keys but also the direct + * or rotary keys. + */ + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + /* For direct keys. */ for (i = 0; i < pdata->direct_key_num; i++) { keycode = pdata->direct_key_map[i]; keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; @@ -177,11 +409,13 @@ static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) } __clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const 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]; @@ -235,7 +469,8 @@ scan: if ((bits_changed & (1 << row)) == 0) continue; - code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); + code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); input_report_key(input_dev, keypad->keycodes[code], new_state[col] & (1 << row)); @@ -283,7 +518,7 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; uint32_t kprec; /* read and reset to default count value */ @@ -299,7 +534,7 @@ static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const 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; @@ -310,7 +545,15 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) if (pdata->enable_rotary0 || pdata->enable_rotary1) pxa27x_keypad_scan_rotary(keypad); - new_state = KPDK_DK(kpdk) & keypad->direct_key_mask; + /* + * 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) @@ -329,11 +572,21 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) keypad->direct_key_state = new_state; } +static void clear_wakeup_event(struct pxa27x_keypad *keypad) +{ + const 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); @@ -345,10 +598,13 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const 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; @@ -372,7 +628,14 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) if (pdata->direct_key_num > direct_key_num) direct_key_num = pdata->direct_key_num; - keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask; + /* + * 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) @@ -388,7 +651,7 @@ static int pxa27x_keypad_open(struct input_dev *dev) struct pxa27x_keypad *keypad = input_get_drvdata(dev); /* Enable unit clock */ - clk_enable(keypad->clk); + clk_prepare_enable(keypad->clk); pxa27x_keypad_config(keypad); return 0; @@ -399,19 +662,23 @@ static void pxa27x_keypad_close(struct input_dev *dev) struct pxa27x_keypad *keypad = input_get_drvdata(dev); /* Disable clock unit */ - clk_disable(keypad->clk); + clk_disable_unprepare(keypad->clk); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int pxa27x_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); - clk_disable(keypad->clk); - + /* + * 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; } @@ -422,40 +689,45 @@ static int pxa27x_keypad_resume(struct device *dev) struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - if (device_may_wakeup(&pdev->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); - mutex_lock(&input_dev->mutex); + if (input_dev->users) { + /* Enable unit clock */ + clk_prepare_enable(keypad->clk); + pxa27x_keypad_config(keypad); + } - if (input_dev->users) { - /* Enable unit clock */ - clk_enable(keypad->clk); - pxa27x_keypad_config(keypad); + mutex_unlock(&input_dev->mutex); } - 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 __devinit pxa27x_keypad_probe(struct platform_device *pdev) +static SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops, + pxa27x_keypad_suspend, pxa27x_keypad_resume); + + +static int pxa27x_keypad_probe(struct platform_device *pdev) { - struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; + const struct pxa27x_keypad_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; 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"); + /* Driver need build keycode from device tree or pdata */ + if (!np && !pdata) return -EINVAL; - } irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -517,14 +789,30 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) 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) { + error = pxa27x_keypad_build_keycode(keypad); + } else { + error = pxa27x_keypad_build_keycode_from_dt(keypad); + /* + * Data that we get from DT resides in dynamically + * allocated memory so we need to update our pdata + * pointer. + */ + pdata = keypad->pdata; + } + if (error) { + dev_err(&pdev->dev, "failed to build keycode\n"); + goto failed_put_clk; + } + + keypad->row_shift = get_count_order(pdata->matrix_key_cols); 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, IRQF_DISABLED, + error = request_irq(irq, pxa27x_keypad_irq_handler, 0, pdev->name, keypad); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); @@ -544,7 +832,7 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(irq, pdev); + free_irq(irq, keypad); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -557,23 +845,20 @@ failed_free: return error; } -static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) +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); + free_irq(keypad->irq, keypad); clk_put(keypad->clk); input_unregister_device(keypad->input_dev); - input_free_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; @@ -582,30 +867,25 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) /* work with hotplug and coldplug */ MODULE_ALIAS("platform:pxa27x-keypad"); +#ifdef CONFIG_OF +static const struct of_device_id pxa27x_keypad_dt_match[] = { + { .compatible = "marvell,pxa27x-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pxa27x_keypad_dt_match); +#endif + static struct platform_driver pxa27x_keypad_driver = { .probe = pxa27x_keypad_probe, - .remove = __devexit_p(pxa27x_keypad_remove), + .remove = pxa27x_keypad_remove, .driver = { .name = "pxa27x-keypad", + .of_match_table = of_match_ptr(pxa27x_keypad_dt_match), .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &pxa27x_keypad_pm_ops, -#endif }, }; - -static int __init pxa27x_keypad_init(void) -{ - return platform_driver_register(&pxa27x_keypad_driver); -} - -static void __exit pxa27x_keypad_exit(void) -{ - platform_driver_unregister(&pxa27x_keypad_driver); -} - -module_init(pxa27x_keypad_init); -module_exit(pxa27x_keypad_exit); +module_platform_driver(pxa27x_keypad_driver); MODULE_DESCRIPTION("PXA27x Keypad Controller Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/pxa930_rotary.c b/drivers/input/keyboard/pxa930_rotary.c index 95fbba470e6..374ca0246c8 100644 --- a/drivers/input/keyboard/pxa930_rotary.c +++ b/drivers/input/keyboard/pxa930_rotary.c @@ -8,13 +8,13 @@ #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 <mach/pxa930_rotary.h> +#include <linux/platform_data/keyboard-pxa930_rotary.h> #define SBCR (0x04) #define ERCR (0x0c) @@ -81,9 +81,10 @@ static void pxa930_rotary_close(struct input_dev *dev) clear_sbcr(r); } -static int __devinit pxa930_rotary_probe(struct platform_device *pdev) +static int pxa930_rotary_probe(struct platform_device *pdev) { - struct pxa930_rotary_platform_data *pdata = pdev->dev.platform_data; + struct pxa930_rotary_platform_data *pdata = + dev_get_platdata(&pdev->dev); struct pxa930_rotary *r; struct input_dev *input_dev; struct resource *res; @@ -147,7 +148,7 @@ static int __devinit pxa930_rotary_probe(struct platform_device *pdev) r->input_dev = input_dev; input_set_drvdata(input_dev, r); - err = request_irq(irq, rotary_irq, IRQF_DISABLED, + err = request_irq(irq, rotary_irq, 0, "enhanced rotary", r); if (err) { dev_err(&pdev->dev, "failed to request IRQ\n"); @@ -173,14 +174,13 @@ failed_free: return err; } -static int __devexit pxa930_rotary_remove(struct platform_device *pdev) +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; @@ -192,20 +192,9 @@ static struct platform_driver pxa930_rotary_driver = { .owner = THIS_MODULE, }, .probe = pxa930_rotary_probe, - .remove = __devexit_p(pxa930_rotary_remove), + .remove = pxa930_rotary_remove, }; - -static int __init pxa930_rotary_init(void) -{ - return platform_driver_register(&pxa930_rotary_driver); -} -module_init(pxa930_rotary_init); - -static void __exit pxa930_rotary_exit(void) -{ - platform_driver_unregister(&pxa930_rotary_driver); -} -module_exit(pxa930_rotary_exit); +module_platform_driver(pxa930_rotary_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller"); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c new file mode 100644 index 00000000000..52cd6e88acd --- /dev/null +++ b/drivers/input/keyboard/qt1070.c @@ -0,0 +1,292 @@ +/* + * 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/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; +} + +#ifdef CONFIG_PM_SLEEP +static int qt1070_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qt1070_data *data = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + enable_irq_wake(data->irq); + + return 0; +} + +static int qt1070_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qt1070_data *data = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + disable_irq_wake(data->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(qt1070_pm_ops, qt1070_suspend, qt1070_resume); + +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, + .pm = &qt1070_pm_ops, + }, + .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"); diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index 191cc51d6cf..819b22897c1 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -19,7 +19,7 @@ */ #include <linux/kernel.h> -#include <linux/init.h> +#include <linux/leds.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -39,6 +39,11 @@ #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) @@ -49,6 +54,17 @@ static unsigned char qt2160_key2code[] = { 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; @@ -56,8 +72,61 @@ struct qt2160_data { 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) { @@ -156,8 +225,7 @@ static irqreturn_t qt2160_irq(int irq, void *_qt2160) spin_lock_irqsave(&qt2160->lock, flags); - __cancel_delayed_work(&qt2160->dwork); - schedule_delayed_work(&qt2160->dwork, 0); + mod_delayed_work(system_wq, &qt2160->dwork, 0); spin_unlock_irqrestore(&qt2160->lock, flags); @@ -184,7 +252,7 @@ static void qt2160_worker(struct work_struct *work) qt2160_schedule_read(qt2160); } -static int __devinit qt2160_read(struct i2c_client *client, u8 reg) +static int qt2160_read(struct i2c_client *client, u8 reg) { int ret; @@ -205,29 +273,77 @@ static int __devinit qt2160_read(struct i2c_client *client, u8 reg) return ret; } -static int __devinit qt2160_write(struct i2c_client *client, u8 reg, u8 data) +static int qt2160_write(struct i2c_client *client, u8 reg, u8 data) { - int error; + int ret; - error = i2c_smbus_write_byte(client, reg); - if (error) { + ret = i2c_smbus_write_byte_data(client, reg, data); + if (ret < 0) dev_err(&client->dev, - "couldn't send request. Returned %d\n", error); - return error; + "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; } - error = i2c_smbus_write_byte(client, data); - if (error) { - dev_err(&client->dev, - "couldn't write data. Returned %d\n", error); - return error; + /* 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); } +} - return error; +#else + +static inline int qt2160_register_leds(struct qt2160_data *qt2160) +{ + return 0; } +static inline void qt2160_unregister_leds(struct qt2160_data *qt2160) +{ +} -static bool __devinit qt2160_identify(struct i2c_client *client) +#endif + +static bool qt2160_identify(struct i2c_client *client) { int id, ver, rev; @@ -258,8 +374,8 @@ static bool __devinit qt2160_identify(struct i2c_client *client) return true; } -static int __devinit qt2160_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int qt2160_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct qt2160_data *qt2160; struct input_dev *input; @@ -324,11 +440,17 @@ static int __devinit qt2160_probe(struct i2c_client *client, } } + 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_free_irq; + goto err_unregister_leds; } i2c_set_clientdata(client, qt2160); @@ -336,6 +458,8 @@ static int __devinit qt2160_probe(struct i2c_client *client, return 0; +err_unregister_leds: + qt2160_unregister_leds(qt2160); err_free_irq: if (client->irq) free_irq(client->irq, qt2160); @@ -345,10 +469,12 @@ err_free_mem: return error; } -static int __devexit qt2160_remove(struct i2c_client *client) +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); @@ -358,11 +484,10 @@ static int __devexit qt2160_remove(struct i2c_client *client) input_unregister_device(qt2160->input); kfree(qt2160); - i2c_set_clientdata(client, NULL); return 0; } -static struct i2c_device_id qt2160_idtable[] = { +static const struct i2c_device_id qt2160_idtable[] = { { "qt2160", 0, }, { } }; @@ -377,20 +502,10 @@ static struct i2c_driver qt2160_driver = { .id_table = qt2160_idtable, .probe = qt2160_probe, - .remove = __devexit_p(qt2160_remove), + .remove = qt2160_remove, }; -static int __init qt2160_init(void) -{ - return i2c_add_driver(&qt2160_driver); -} -module_init(qt2160_init); - -static void __exit qt2160_cleanup(void) -{ - i2c_del_driver(&qt2160_driver); -} -module_exit(qt2160_cleanup); +module_i2c_driver(qt2160_driver); MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c new file mode 100644 index 00000000000..5e80fbf7b5e --- /dev/null +++ b/drivers/input/keyboard/samsung-keypad.c @@ -0,0 +1,616 @@ +/* + * 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/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/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]; + 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; + + if (!np) { + dev_err(dev, "missing device tree data\n"); + return ERR_PTR(-EINVAL); + } + + 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); + } + + 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 ERR_PTR(-EINVAL); + } + 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 ERR_PTR(-ENOMEM); + } + 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 ERR_PTR(-ENOMEM); + } + 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; +} +#else +static struct samsung_keypad_platdata * +samsung_keypad_parse_dt(struct device *dev) +{ + dev_err(dev, "no platform data defined\n"); + + return ERR_PTR(-EINVAL); +} +#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; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + pdata = samsung_keypad_parse_dt(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + 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) + keypad->type = of_device_is_compatible(pdev->dev.of_node, + "samsung,s5pv210-keypad"); + 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); +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); + + 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"); diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 8e9380bfed4..7abf03b4cc9 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -12,23 +12,16 @@ #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/clk.h> +#include <linux/bitmap.h> +#include <linux/pm_runtime.h> #include <linux/io.h> - -#define KYCR1_OFFS 0x00 -#define KYCR2_OFFS 0x04 -#define KYINDR_OFFS 0x08 -#define KYOUTDR_OFFS 0x0c - -#define KYCR2_IRQ_LEVEL 0x10 -#define KYCR2_IRQ_DISABLED 0x00 +#include <linux/slab.h> static const struct { unsigned char kymd, keyout, keyin; @@ -36,84 +29,130 @@ static const struct { [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; - struct clk *clk; - unsigned long last_keys; + 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; - unsigned long keys, keys1, keys0, mask; + 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; + int i, k, n; dev_dbg(&pdev->dev, "isr!\n"); - keys1 = ~0; - keys0 = 0; + bitmap_fill(keys1, SH_KEYSC_MAXKEYS); + bitmap_zero(keys0, SH_KEYSC_MAXKEYS); do { - keys = 0; + bitmap_zero(keys, SH_KEYSC_MAXKEYS); keyin_set = 0; - iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS); + sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED); + + for (i = 0; i < keyout_nr; i++) { + n = keyin_nr * i; - for (i = 0; i < sh_keysc_mode[pdata->mode].keyout; i++) { - iowrite16(0xfff ^ (3 << (i * 2)), - priv->iomem_base + KYOUTDR_OFFS); + /* drive one KEYOUT pin low, read KEYIN pins */ + sh_keysc_write(priv, KYOUTDR, 0xffff ^ (3 << (i * 2))); udelay(pdata->delay); - tmp = ioread16(priv->iomem_base + KYINDR_OFFS); - keys |= tmp << (sh_keysc_mode[pdata->mode].keyin * i); - tmp ^= (1 << sh_keysc_mode[pdata->mode].keyin) - 1; - keyin_set |= tmp; - } + tmp = sh_keysc_read(priv, KYINDR); - iowrite16(0, priv->iomem_base + KYOUTDR_OFFS); - iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8), - priv->iomem_base + KYCR2_OFFS); + /* set bit if key press has been detected */ + for (k = 0; k < keyin_nr; k++) { + if (tmp & (1 << k)) + __set_bit(n + k, keys); + } - if (pdata->kycr2_delay) - udelay(pdata->kycr2_delay); + /* keep track of which KEYIN bits that have been set */ + keyin_set |= tmp ^ ((1 << keyin_nr) - 1); + } + + sh_keysc_level_mode(priv, keyin_set); - keys ^= ~0; - keys &= (1 << (sh_keysc_mode[pdata->mode].keyin * - sh_keysc_mode[pdata->mode].keyout)) - 1; - keys1 &= keys; - keys0 |= keys; + bitmap_complement(keys, keys, SH_KEYSC_MAXKEYS); + bitmap_and(keys1, keys1, keys, SH_KEYSC_MAXKEYS); + bitmap_or(keys0, keys0, keys, SH_KEYSC_MAXKEYS); - dev_dbg(&pdev->dev, "keys 0x%08lx\n", keys); + sh_keysc_map_dbg(&pdev->dev, keys, "keys"); - } while (ioread16(priv->iomem_base + KYCR2_OFFS) & 0x01); + } while (sh_keysc_read(priv, KYCR2) & 0x01); - dev_dbg(&pdev->dev, "last_keys 0x%08lx keys0 0x%08lx keys1 0x%08lx\n", - priv->last_keys, keys0, keys1); + 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; - mask = 1 << i; - - if (!((priv->last_keys ^ keys0) & mask)) + if (test_bit(i, keys0) == test_bit(i, priv->last_keys)) continue; - if ((keys1 | keys0) & mask) { + if (test_bit(i, keys1) || test_bit(i, keys0)) { input_event(priv->input, EV_KEY, k, 1); - priv->last_keys |= mask; + __set_bit(i, priv->last_keys); } - if (!(keys1 & mask)) { + if (!test_bit(i, keys1)) { input_event(priv->input, EV_KEY, k, 0); - priv->last_keys &= ~mask; + __clear_bit(i, priv->last_keys); } } @@ -122,19 +161,16 @@ static irqreturn_t sh_keysc_isr(int irq, void *dev_id) return IRQ_HANDLED; } -#define res_size(res) ((res)->end - (res)->start + 1) - -static int __devinit sh_keysc_probe(struct platform_device *pdev) +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; - char clk_name[8]; int i; int irq, error; - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data defined\n"); error = -EINVAL; goto err0; @@ -161,29 +197,21 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, priv); - memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata)); + memcpy(&priv->pdata, dev_get_platdata(&pdev->dev), sizeof(priv->pdata)); pdata = &priv->pdata; - priv->iomem_base = ioremap_nocache(res->start, res_size(res)); + 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; } - snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - error = PTR_ERR(priv->clk); - goto err2; - } - priv->input = input_allocate_device(); if (!priv->input) { dev_err(&pdev->dev, "failed to allocate input device\n"); error = -ENOMEM; - goto err3; + goto err2; } input = priv->input; @@ -202,10 +230,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) input->keycodesize = sizeof(pdata->keycodes[0]); input->keycodemax = ARRAY_SIZE(pdata->keycodes); - error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); + 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 err4; + goto err3; } for (i = 0; i < SH_KEYSC_MAXKEYS; i++) @@ -215,54 +244,51 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err5; + goto err4; } - clk_enable(priv->clk); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); - iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) | - pdata->scan_timing, priv->iomem_base + KYCR1_OFFS); - iowrite16(0, priv->iomem_base + KYOUTDR_OFFS); - iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS); + 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; - err5: - free_irq(irq, pdev); err4: - input_free_device(input); + free_irq(irq, pdev); err3: - clk_put(priv->clk); + input_free_device(input); err2: iounmap(priv->iomem_base); err1: - platform_set_drvdata(pdev, NULL); kfree(priv); err0: return error; } -static int __devexit sh_keysc_remove(struct platform_device *pdev) +static int sh_keysc_remove(struct platform_device *pdev) { struct sh_keysc_priv *priv = platform_get_drvdata(pdev); - iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS); + 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); - clk_disable(priv->clk); - clk_put(priv->clk); + 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); @@ -270,17 +296,16 @@ static int sh_keysc_suspend(struct device *dev) int irq = platform_get_irq(pdev, 0); unsigned short value; - value = ioread16(priv->iomem_base + KYCR1_OFFS); + value = sh_keysc_read(priv, KYCR1); if (device_may_wakeup(dev)) { - value |= 0x80; + sh_keysc_write(priv, KYCR1, value | 0x80); enable_irq_wake(irq); } else { - value &= ~0x80; + sh_keysc_write(priv, KYCR1, value & ~0x80); + pm_runtime_put_sync(dev); } - iowrite16(value, priv->iomem_base + KYCR1_OFFS); - return 0; } @@ -291,36 +316,25 @@ static int sh_keysc_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); + else + pm_runtime_get_sync(dev); return 0; } +#endif -static const struct dev_pm_ops sh_keysc_dev_pm_ops = { - .suspend = sh_keysc_suspend, - .resume = sh_keysc_resume, -}; +static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops, + sh_keysc_suspend, sh_keysc_resume); -struct platform_driver sh_keysc_device_driver = { +static struct platform_driver sh_keysc_device_driver = { .probe = sh_keysc_probe, - .remove = __devexit_p(sh_keysc_remove), + .remove = sh_keysc_remove, .driver = { .name = "sh_keysc", .pm = &sh_keysc_dev_pm_ops, } }; - -static int __init sh_keysc_init(void) -{ - return platform_driver_register(&sh_keysc_device_driver); -} - -static void __exit sh_keysc_exit(void) -{ - platform_driver_unregister(&sh_keysc_device_driver); -} - -module_init(sh_keysc_init); -module_exit(sh_keysc_exit); +module_platform_driver(sh_keysc_device_driver); MODULE_AUTHOR("Magnus Damm"); MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver"); diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c new file mode 100644 index 00000000000..258af10e581 --- /dev/null +++ b/drivers/input/keyboard/spear-keyboard.c @@ -0,0 +1,397 @@ +/* + * 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/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; + + 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; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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); + + 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"); diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c deleted file mode 100644 index 13967422658..00000000000 --- a/drivers/input/keyboard/spitzkbd.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Keyboard driver for Sharp Spitz, Borzoi and Akita (SL-Cxx00 series) - * - * Copyright (c) 2005 Richard Purdie - * - * 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/delay.h> -#include <linux/platform_device.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/slab.h> - -#include <mach/spitz.h> -#include <mach/pxa2xx-gpio.h> - -#define KB_ROWS 7 -#define KB_COLS 11 -#define KB_ROWMASK(r) (1 << (r)) -#define SCANCODE(r,c) (((r)<<4) + (c) + 1) -#define NR_SCANCODES ((KB_ROWS<<4) + 1) - -#define SCAN_INTERVAL (50) /* ms */ -#define HINGE_SCAN_INTERVAL (150) /* ms */ - -#define SPITZ_KEY_CALENDER KEY_F1 -#define SPITZ_KEY_ADDRESS KEY_F2 -#define SPITZ_KEY_FN KEY_F3 -#define SPITZ_KEY_CANCEL KEY_F4 -#define SPITZ_KEY_EXOK KEY_F5 -#define SPITZ_KEY_EXCANCEL KEY_F6 -#define SPITZ_KEY_EXJOGDOWN KEY_F7 -#define SPITZ_KEY_EXJOGUP KEY_F8 -#define SPITZ_KEY_JAP1 KEY_LEFTALT -#define SPITZ_KEY_JAP2 KEY_RIGHTCTRL -#define SPITZ_KEY_SYNC KEY_F9 -#define SPITZ_KEY_MAIL KEY_F10 -#define SPITZ_KEY_OK KEY_F11 -#define SPITZ_KEY_MENU KEY_F12 - -static unsigned char spitzkbd_keycode[NR_SCANCODES] = { - 0, /* 0 */ - KEY_LEFTCTRL, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, SPITZ_KEY_EXOK, SPITZ_KEY_EXCANCEL, 0, 0, 0, 0, 0, /* 1-16 */ - 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, SPITZ_KEY_EXJOGDOWN, SPITZ_KEY_EXJOGUP, 0, 0, 0, 0, 0, /* 17-32 */ - KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ - SPITZ_KEY_ADDRESS, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ - SPITZ_KEY_CALENDER, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 65-80 */ - SPITZ_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, SPITZ_KEY_FN, 0, 0, 0, 0, 0, /* 81-96 */ - KEY_SYSRQ, SPITZ_KEY_JAP1, SPITZ_KEY_JAP2, SPITZ_KEY_CANCEL, SPITZ_KEY_OK, SPITZ_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0 /* 97-112 */ -}; - -static int spitz_strobes[] = { - SPITZ_GPIO_KEY_STROBE0, - SPITZ_GPIO_KEY_STROBE1, - SPITZ_GPIO_KEY_STROBE2, - SPITZ_GPIO_KEY_STROBE3, - SPITZ_GPIO_KEY_STROBE4, - SPITZ_GPIO_KEY_STROBE5, - SPITZ_GPIO_KEY_STROBE6, - SPITZ_GPIO_KEY_STROBE7, - SPITZ_GPIO_KEY_STROBE8, - SPITZ_GPIO_KEY_STROBE9, - SPITZ_GPIO_KEY_STROBE10, -}; - -static int spitz_senses[] = { - SPITZ_GPIO_KEY_SENSE0, - SPITZ_GPIO_KEY_SENSE1, - SPITZ_GPIO_KEY_SENSE2, - SPITZ_GPIO_KEY_SENSE3, - SPITZ_GPIO_KEY_SENSE4, - SPITZ_GPIO_KEY_SENSE5, - SPITZ_GPIO_KEY_SENSE6, -}; - -struct spitzkbd { - unsigned char keycode[ARRAY_SIZE(spitzkbd_keycode)]; - struct input_dev *input; - char phys[32]; - - spinlock_t lock; - struct timer_list timer; - struct timer_list htimer; - - unsigned int suspended; - unsigned long suspend_jiffies; -}; - -#define KB_DISCHARGE_DELAY 10 -#define KB_ACTIVATE_DELAY 10 - -/* Helper functions for reading the keyboard matrix - * Note: We should really be using the generic gpio functions to alter - * GPDR but it requires a function call per GPIO bit which is - * excessive when we need to access 11 bits at once, multiple times. - * These functions must be called within local_irq_save()/local_irq_restore() - * or similar. - */ -static inline void spitzkbd_discharge_all(void) -{ - /* STROBE All HiZ */ - GPCR0 = SPITZ_GPIO_G0_STROBE_BIT; - GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; - GPCR1 = SPITZ_GPIO_G1_STROBE_BIT; - GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; - GPCR2 = SPITZ_GPIO_G2_STROBE_BIT; - GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; - GPCR3 = SPITZ_GPIO_G3_STROBE_BIT; - GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; -} - -static inline void spitzkbd_activate_all(void) -{ - /* STROBE ALL -> High */ - GPSR0 = SPITZ_GPIO_G0_STROBE_BIT; - GPDR0 |= SPITZ_GPIO_G0_STROBE_BIT; - GPSR1 = SPITZ_GPIO_G1_STROBE_BIT; - GPDR1 |= SPITZ_GPIO_G1_STROBE_BIT; - GPSR2 = SPITZ_GPIO_G2_STROBE_BIT; - GPDR2 |= SPITZ_GPIO_G2_STROBE_BIT; - GPSR3 = SPITZ_GPIO_G3_STROBE_BIT; - GPDR3 |= SPITZ_GPIO_G3_STROBE_BIT; - - udelay(KB_DISCHARGE_DELAY); - - /* Clear any interrupts we may have triggered when altering the GPIO lines */ - GEDR0 = SPITZ_GPIO_G0_SENSE_BIT; - GEDR1 = SPITZ_GPIO_G1_SENSE_BIT; - GEDR2 = SPITZ_GPIO_G2_SENSE_BIT; - GEDR3 = SPITZ_GPIO_G3_SENSE_BIT; -} - -static inline void spitzkbd_activate_col(int col) -{ - int gpio = spitz_strobes[col]; - GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; - GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; - GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; - GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; - GPSR(gpio) = GPIO_bit(gpio); - GPDR(gpio) |= GPIO_bit(gpio); -} - -static inline void spitzkbd_reset_col(int col) -{ - int gpio = spitz_strobes[col]; - GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; - GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; - GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; - GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; - GPCR(gpio) = GPIO_bit(gpio); - GPDR(gpio) |= GPIO_bit(gpio); -} - -static inline int spitzkbd_get_row_status(int col) -{ - return ((GPLR0 >> 12) & 0x01) | ((GPLR0 >> 16) & 0x02) - | ((GPLR2 >> 25) & 0x04) | ((GPLR1 << 1) & 0x08) - | ((GPLR1 >> 0) & 0x10) | ((GPLR1 >> 1) & 0x60); -} - -/* - * The spitz keyboard only generates interrupts when a key is pressed. - * When a key is pressed, we enable a timer which then scans the - * keyboard to detect when the key is released. - */ - -/* Scan the hardware keyboard and push any changes up through the input layer */ -static void spitzkbd_scankeyboard(struct spitzkbd *spitzkbd_data) -{ - unsigned int row, col, rowd; - unsigned long flags; - unsigned int num_pressed, pwrkey = ((GPLR(SPITZ_GPIO_ON_KEY) & GPIO_bit(SPITZ_GPIO_ON_KEY)) != 0); - - if (spitzkbd_data->suspended) - return; - - spin_lock_irqsave(&spitzkbd_data->lock, flags); - - num_pressed = 0; - for (col = 0; col < KB_COLS; col++) { - /* - * Discharge the output driver capacitatance - * in the keyboard matrix. (Yes it is significant..) - */ - - spitzkbd_discharge_all(); - udelay(KB_DISCHARGE_DELAY); - - spitzkbd_activate_col(col); - udelay(KB_ACTIVATE_DELAY); - - rowd = spitzkbd_get_row_status(col); - for (row = 0; row < KB_ROWS; row++) { - unsigned int scancode, pressed; - - scancode = SCANCODE(row, col); - pressed = rowd & KB_ROWMASK(row); - - input_report_key(spitzkbd_data->input, spitzkbd_data->keycode[scancode], pressed); - - if (pressed) - num_pressed++; - } - spitzkbd_reset_col(col); - } - - spitzkbd_activate_all(); - - input_report_key(spitzkbd_data->input, SPITZ_KEY_SYNC, (GPLR(SPITZ_GPIO_SYNC) & GPIO_bit(SPITZ_GPIO_SYNC)) != 0 ); - input_report_key(spitzkbd_data->input, KEY_SUSPEND, pwrkey); - - if (pwrkey && time_after(jiffies, spitzkbd_data->suspend_jiffies + msecs_to_jiffies(1000))) { - input_event(spitzkbd_data->input, EV_PWR, KEY_SUSPEND, 1); - spitzkbd_data->suspend_jiffies = jiffies; - } - - input_sync(spitzkbd_data->input); - - /* if any keys are pressed, enable the timer */ - if (num_pressed) - mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL)); - - spin_unlock_irqrestore(&spitzkbd_data->lock, flags); -} - -/* - * spitz keyboard interrupt handler. - */ -static irqreturn_t spitzkbd_interrupt(int irq, void *dev_id) -{ - struct spitzkbd *spitzkbd_data = dev_id; - - if (!timer_pending(&spitzkbd_data->timer)) { - /** wait chattering delay **/ - udelay(20); - spitzkbd_scankeyboard(spitzkbd_data); - } - - return IRQ_HANDLED; -} - -/* - * spitz timer checking for released keys - */ -static void spitzkbd_timer_callback(unsigned long data) -{ - struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data; - - spitzkbd_scankeyboard(spitzkbd_data); -} - -/* - * The hinge switches generate an interrupt. - * We debounce the switches and pass them to the input system. - */ - -static irqreturn_t spitzkbd_hinge_isr(int irq, void *dev_id) -{ - struct spitzkbd *spitzkbd_data = dev_id; - - if (!timer_pending(&spitzkbd_data->htimer)) - mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); - - return IRQ_HANDLED; -} - -#define HINGE_STABLE_COUNT 2 -static int sharpsl_hinge_state; -static int hinge_count; - -static void spitzkbd_hinge_timer(unsigned long data) -{ - struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data; - unsigned long state; - unsigned long flags; - - state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB)); - state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)); - if (state != sharpsl_hinge_state) { - hinge_count = 0; - sharpsl_hinge_state = state; - } else if (hinge_count < HINGE_STABLE_COUNT) { - hinge_count++; - } - - if (hinge_count >= HINGE_STABLE_COUNT) { - spin_lock_irqsave(&spitzkbd_data->lock, flags); - - input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0)); - input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0)); - input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0)); - input_sync(spitzkbd_data->input); - - spin_unlock_irqrestore(&spitzkbd_data->lock, flags); - } else { - mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); - } -} - -#ifdef CONFIG_PM -static int spitzkbd_suspend(struct platform_device *dev, pm_message_t state) -{ - int i; - struct spitzkbd *spitzkbd = platform_get_drvdata(dev); - spitzkbd->suspended = 1; - - /* Set Strobe lines as inputs - *except* strobe line 0 leave this - enabled so we can detect a power button press for resume */ - for (i = 1; i < SPITZ_KEY_STROBE_NUM; i++) - pxa_gpio_mode(spitz_strobes[i] | GPIO_IN); - - return 0; -} - -static int spitzkbd_resume(struct platform_device *dev) -{ - int i; - struct spitzkbd *spitzkbd = platform_get_drvdata(dev); - - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++) - pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH); - - /* Upon resume, ignore the suspend key for a short while */ - spitzkbd->suspend_jiffies = jiffies; - spitzkbd->suspended = 0; - - return 0; -} -#else -#define spitzkbd_suspend NULL -#define spitzkbd_resume NULL -#endif - -static int __devinit spitzkbd_probe(struct platform_device *dev) -{ - struct spitzkbd *spitzkbd; - struct input_dev *input_dev; - int i, err = -ENOMEM; - - spitzkbd = kzalloc(sizeof(struct spitzkbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!spitzkbd || !input_dev) - goto fail; - - platform_set_drvdata(dev, spitzkbd); - strcpy(spitzkbd->phys, "spitzkbd/input0"); - - spin_lock_init(&spitzkbd->lock); - - /* Init Keyboard rescan timer */ - init_timer(&spitzkbd->timer); - spitzkbd->timer.function = spitzkbd_timer_callback; - spitzkbd->timer.data = (unsigned long) spitzkbd; - - /* Init Hinge Timer */ - init_timer(&spitzkbd->htimer); - spitzkbd->htimer.function = spitzkbd_hinge_timer; - spitzkbd->htimer.data = (unsigned long) spitzkbd; - - spitzkbd->suspend_jiffies = jiffies; - - spitzkbd->input = input_dev; - - input_dev->name = "Spitz Keyboard"; - input_dev->phys = spitzkbd->phys; - input_dev->dev.parent = &dev->dev; - - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0100; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | - BIT_MASK(EV_PWR) | BIT_MASK(EV_SW); - input_dev->keycode = spitzkbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); - input_dev->keycodemax = ARRAY_SIZE(spitzkbd_keycode); - - memcpy(spitzkbd->keycode, spitzkbd_keycode, sizeof(spitzkbd->keycode)); - for (i = 0; i < ARRAY_SIZE(spitzkbd_keycode); i++) - set_bit(spitzkbd->keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); - set_bit(KEY_SUSPEND, input_dev->keybit); - set_bit(SW_LID, input_dev->swbit); - set_bit(SW_TABLET_MODE, input_dev->swbit); - set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); - - err = input_register_device(input_dev); - if (err) - goto fail; - - mod_timer(&spitzkbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); - - /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++) { - pxa_gpio_mode(spitz_senses[i] | GPIO_IN); - if (request_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd_interrupt, - IRQF_DISABLED|IRQF_TRIGGER_RISING, - "Spitzkbd Sense", spitzkbd)) - printk(KERN_WARNING "spitzkbd: Can't get Sense IRQ: %d!\n", i); - } - - /* Set Strobe lines as outputs - set high */ - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++) - pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH); - - pxa_gpio_mode(SPITZ_GPIO_SYNC | GPIO_IN); - pxa_gpio_mode(SPITZ_GPIO_ON_KEY | GPIO_IN); - pxa_gpio_mode(SPITZ_GPIO_SWA | GPIO_IN); - pxa_gpio_mode(SPITZ_GPIO_SWB | GPIO_IN); - - request_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd_interrupt, - IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Spitzkbd Sync", spitzkbd); - request_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd_interrupt, - IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Spitzkbd PwrOn", spitzkbd); - request_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd_hinge_isr, - IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Spitzkbd SWA", spitzkbd); - request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr, - IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Spitzkbd SWB", spitzkbd); - request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr, - IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Spitzkbd HP", spitzkbd); - - return 0; - - fail: input_free_device(input_dev); - kfree(spitzkbd); - return err; -} - -static int __devexit spitzkbd_remove(struct platform_device *dev) -{ - int i; - struct spitzkbd *spitzkbd = platform_get_drvdata(dev); - - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++) - free_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd); - - free_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd); - free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd); - free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd); - free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd); - free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd); - - del_timer_sync(&spitzkbd->htimer); - del_timer_sync(&spitzkbd->timer); - - input_unregister_device(spitzkbd->input); - - kfree(spitzkbd); - - return 0; -} - -static struct platform_driver spitzkbd_driver = { - .probe = spitzkbd_probe, - .remove = __devexit_p(spitzkbd_remove), - .suspend = spitzkbd_suspend, - .resume = spitzkbd_resume, - .driver = { - .name = "spitz-keyboard", - .owner = THIS_MODULE, - }, -}; - -static int __init spitzkbd_init(void) -{ - return platform_driver_register(&spitzkbd_driver); -} - -static void __exit spitzkbd_exit(void) -{ - platform_driver_unregister(&spitzkbd_driver); -} - -module_init(spitzkbd_init); -module_exit(spitzkbd_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); -MODULE_DESCRIPTION("Spitz Keyboard Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:spitz-keyboard"); diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c new file mode 100644 index 00000000000..de7be4f03d9 --- /dev/null +++ b/drivers/input/keyboard/st-keyscan.c @@ -0,0 +1,276 @@ +/* + * STMicroelectronics Key Scanning driver + * + * Copyright (c) 2014 STMicroelectonics Ltd. + * Author: Stuart Menefy <stuart.menefy@st.com> + * + * Based on sh_keysc.c, copyright 2008 Magnus Damm + * + * 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/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/input/matrix_keypad.h> + +#define ST_KEYSCAN_MAXKEYS 16 + +#define KEYSCAN_CONFIG_OFF 0x0 +#define KEYSCAN_CONFIG_ENABLE 0x1 +#define KEYSCAN_DEBOUNCE_TIME_OFF 0x4 +#define KEYSCAN_MATRIX_STATE_OFF 0x8 +#define KEYSCAN_MATRIX_DIM_OFF 0xc +#define KEYSCAN_MATRIX_DIM_X_SHIFT 0x0 +#define KEYSCAN_MATRIX_DIM_Y_SHIFT 0x2 + +struct st_keyscan { + void __iomem *base; + int irq; + struct clk *clk; + struct input_dev *input_dev; + unsigned long last_state; + unsigned int n_rows; + unsigned int n_cols; + unsigned int debounce_us; +}; + +static irqreturn_t keyscan_isr(int irq, void *dev_id) +{ + struct st_keyscan *keypad = dev_id; + unsigned short *keycode = keypad->input_dev->keycode; + unsigned long state, change; + int bit_nr; + + state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff; + change = keypad->last_state ^ state; + keypad->last_state = state; + + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) + input_report_key(keypad->input_dev, + keycode[bit_nr], state & BIT(bit_nr)); + + input_sync(keypad->input_dev); + + return IRQ_HANDLED; +} + +static int keyscan_start(struct st_keyscan *keypad) +{ + int error; + + error = clk_enable(keypad->clk); + if (error) + return error; + + writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000), + keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF); + + writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) | + ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT), + keypad->base + KEYSCAN_MATRIX_DIM_OFF); + + writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF); + + return 0; +} + +static void keyscan_stop(struct st_keyscan *keypad) +{ + writel(0, keypad->base + KEYSCAN_CONFIG_OFF); + + clk_disable(keypad->clk); +} + +static int keyscan_open(struct input_dev *dev) +{ + struct st_keyscan *keypad = input_get_drvdata(dev); + + return keyscan_start(keypad); +} + +static void keyscan_close(struct input_dev *dev) +{ + struct st_keyscan *keypad = input_get_drvdata(dev); + + keyscan_stop(keypad); +} + +static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data) +{ + struct device *dev = keypad_data->input_dev->dev.parent; + struct device_node *np = dev->of_node; + int error; + + error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows, + &keypad_data->n_cols); + if (error) { + dev_err(dev, "failed to parse keypad params\n"); + return error; + } + + of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us); + + dev_dbg(dev, "n_rows=%d n_col=%d debounce=%d\n", + keypad_data->n_rows, keypad_data->n_cols, + keypad_data->debounce_us); + + return 0; +} + +static int keyscan_probe(struct platform_device *pdev) +{ + struct st_keyscan *keypad_data; + struct input_dev *input_dev; + struct resource *res; + int error; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "no DT data present\n"); + return -EINVAL; + } + + keypad_data = devm_kzalloc(&pdev->dev, sizeof(*keypad_data), + GFP_KERNEL); + if (!keypad_data) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + input_dev->name = pdev->name; + input_dev->phys = "keyscan-keys/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->open = keyscan_open; + input_dev->close = keyscan_close; + + input_dev->id.bustype = BUS_HOST; + + error = keypad_matrix_key_parse_dt(keypad_data); + if (error) + return error; + + error = matrix_keypad_build_keymap(NULL, NULL, + keypad_data->n_rows, + keypad_data->n_cols, + NULL, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return error; + } + + input_set_drvdata(input_dev, keypad_data); + + keypad_data->input_dev = input_dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + keypad_data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(keypad_data->base)) + return PTR_ERR(keypad_data->base); + + keypad_data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad_data->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return PTR_ERR(keypad_data->clk); + } + + error = clk_enable(keypad_data->clk); + if (error) { + dev_err(&pdev->dev, "failed to enable clock\n"); + return error; + } + + keyscan_stop(keypad_data); + + keypad_data->irq = platform_get_irq(pdev, 0); + if (keypad_data->irq < 0) { + dev_err(&pdev->dev, "no IRQ specified\n"); + return -EINVAL; + } + + error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0, + pdev->name, keypad_data); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return error; + } + + 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_data); + + device_set_wakeup_capable(&pdev->dev, 1); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int keyscan_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct st_keyscan *keypad = platform_get_drvdata(pdev); + struct input_dev *input = keypad->input_dev; + + mutex_lock(&input->mutex); + + if (device_may_wakeup(dev)) + enable_irq_wake(keypad->irq); + else if (input->users) + keyscan_stop(keypad); + + mutex_unlock(&input->mutex); + return 0; +} + +static int keyscan_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct st_keyscan *keypad = platform_get_drvdata(pdev); + struct input_dev *input = keypad->input_dev; + int retval = 0; + + mutex_lock(&input->mutex); + + if (device_may_wakeup(dev)) + disable_irq_wake(keypad->irq); + else if (input->users) + retval = keyscan_start(keypad); + + mutex_unlock(&input->mutex); + return retval; +} +#endif + +static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume); + +static const struct of_device_id keyscan_of_match[] = { + { .compatible = "st,sti-keyscan" }, + { }, +}; +MODULE_DEVICE_TABLE(of, keyscan_of_match); + +static struct platform_driver keyscan_device_driver = { + .probe = keyscan_probe, + .driver = { + .name = "st-keyscan", + .pm = &keyscan_dev_pm_ops, + .of_match_table = of_match_ptr(keyscan_of_match), + } +}; + +module_platform_driver(keyscan_device_driver); + +MODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics keyscan device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c new file mode 100644 index 00000000000..c6727dda68f --- /dev/null +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -0,0 +1,398 @@ +/* + * 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/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>"); diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c index 7437219370b..a6e0d565e30 100644 --- a/drivers/input/keyboard/stowaway.c +++ b/drivers/input/keyboard/stowaway.c @@ -32,7 +32,6 @@ #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" @@ -170,15 +169,4 @@ static struct serio_driver skbd_drv = { .disconnect = skbd_disconnect, }; -static int __init skbd_init(void) -{ - return serio_register_driver(&skbd_drv); -} - -static void __exit skbd_exit(void) -{ - serio_unregister_driver(&skbd_drv); -} - -module_init(skbd_init); -module_exit(skbd_exit); +module_serio_driver(skbd_drv); diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index a99a04b03ee..dc6bb9d5b4f 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -31,7 +31,6 @@ #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> @@ -369,19 +368,4 @@ static struct serio_driver sunkbd_drv = { .disconnect = sunkbd_disconnect, }; -/* - * The functions for insering/removing us as a module. - */ - -static int __init sunkbd_init(void) -{ - return serio_register_driver(&sunkbd_drv); -} - -static void __exit sunkbd_exit(void) -{ - serio_unregister_driver(&sunkbd_drv); -} - -module_init(sunkbd_init); -module_exit(sunkbd_exit); +module_serio_driver(sunkbd_drv); diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c new file mode 100644 index 00000000000..ad7abae6907 --- /dev/null +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -0,0 +1,519 @@ +/* + * 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/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); +} + +#ifdef CONFIG_OF +static const struct tc3589x_keypad_platform_data * +tc3589x_keypad_of_probe(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct tc3589x_keypad_platform_data *plat; + u32 cols, rows; + u32 debounce_ms; + int proplen; + + 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, "keypad,num-columns", &cols); + of_property_read_u32(np, "keypad,num-rows", &rows); + plat->kcol = (u8) cols; + plat->krow = (u8) rows; + if (!plat->krow || !plat->kcol || + plat->krow > TC_KPD_ROWS || plat->kcol > TC_KPD_COLUMNS) { + dev_err(dev, + "keypad columns/rows not properly specified (%ux%u)\n", + plat->kcol, plat->krow); + return ERR_PTR(-EINVAL); + } + + if (!of_get_property(np, "linux,keymap", &proplen)) { + dev_err(dev, "property linux,keymap not found\n"); + return ERR_PTR(-ENOENT); + } + + plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat"); + plat->enable_wakeup = of_property_read_bool(np, "linux,wakeup"); + + /* The custom delay format is ms/16 */ + of_property_read_u32(np, "debounce-delay-ms", &debounce_ms); + if (debounce_ms) + plat->debounce_period = debounce_ms * 16; + else + plat->debounce_period = TC_KPD_DEBOUNCE_PERIOD; + + plat->settle_time = TC_KPD_SETTLE_TIME; + /* FIXME: should be property of the IRQ resource? */ + plat->irqtype = IRQF_TRIGGER_FALLING; + + return plat; +} +#else +static inline const struct tc3589x_keypad_platform_data * +tc3589x_keypad_of_probe(struct device *dev) +{ + return ERR_PTR(-ENODEV); +} +#endif + + +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) { + plat = tc3589x_keypad_of_probe(&pdev->dev); + if (IS_ERR(plat)) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return PTR_ERR(plat); + } + } + + 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"); diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c new file mode 100644 index 00000000000..dc983ab6c0a --- /dev/null +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -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, ®_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 = dev_get_platdata(&client->dev); + 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"); diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c new file mode 100644 index 00000000000..4e491c1762c --- /dev/null +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -0,0 +1,428 @@ +/* + * 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, ®); + + /* 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, ®); + } + + 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, ®); + 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); + +/* + * The device tree based i2c loader looks for + * "i2c:" + second_component_of(property("compatible")) + * and therefore we need an alias to be found. + */ +MODULE_ALIAS("i2c:tca8418"); +#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"); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c new file mode 100644 index 00000000000..9757a58bc89 --- /dev/null +++ b/drivers/input/keyboard/tegra-kbc.c @@ -0,0 +1,835 @@ +/* + * 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/reset.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; + struct reset_control *rst; + 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; + + /* Either mask all keys or none. */ + rst_val = (filter && !kbc->wakeup) ? ~0 : 0; + + for (i = 0; i < kbc->hw_support->max_rows; i++) + writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); +} + +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.*/ + reset_control_assert(kbc->rst); + udelay(100); + reset_control_assert(kbc->rst); + 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(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; + + 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); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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); + } + + kbc->rst = devm_reset_control_get(&pdev->dev, "kbc"); + if (IS_ERR(kbc->rst)) { + dev_err(&pdev->dev, "failed to get keyboard reset\n"); + return PTR_ERR(kbc->rst); + } + + /* + * 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"); diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c deleted file mode 100644 index 42cb3faf733..00000000000 --- a/drivers/input/keyboard/tosakbd.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Keyboard driver for Sharp Tosa models (SL-6000x) - * - * Copyright (c) 2005 Dirk Opfer - * Copyright (c) 2007 Dmitry Baryshkov - * - * Based on xtkbd.c/locomkbd.c/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/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/input.h> -#include <linux/delay.h> -#include <linux/interrupt.h> - -#include <mach/gpio.h> -#include <mach/tosa.h> - -#define KB_ROWMASK(r) (1 << (r)) -#define SCANCODE(r, c) (((r)<<4) + (c) + 1) -#define NR_SCANCODES SCANCODE(TOSA_KEY_SENSE_NUM - 1, TOSA_KEY_STROBE_NUM - 1) + 1 - -#define SCAN_INTERVAL (HZ/10) - -#define KB_DISCHARGE_DELAY 10 -#define KB_ACTIVATE_DELAY 10 - -static unsigned short tosakbd_keycode[NR_SCANCODES] = { -0, -0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, -0, 0, 0, 0, 0, 0, 0, 0, -KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, -0, 0, 0, 0, 0, 0, 0, 0, -KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, -0, 0, 0, 0, 0, 0, 0, 0, -KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESSBOOK, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, -KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, 0, -KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDAR, TOSA_KEY_HOMEPAGE, KEY_LEFTCTRL, TOSA_KEY_LIGHT, -0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, -KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, -0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, -KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, -0, 0, 0, -}; - -struct tosakbd { - unsigned short keycode[ARRAY_SIZE(tosakbd_keycode)]; - struct input_dev *input; - bool suspended; - spinlock_t lock; /* protect kbd scanning */ - struct timer_list timer; -}; - - -/* Helper functions for reading the keyboard matrix - * Note: We should really be using the generic gpio functions to alter - * GPDR but it requires a function call per GPIO bit which is - * excessive when we need to access 12 bits at once, multiple times. - * These functions must be called within local_irq_save()/local_irq_restore() - * or similar. - */ -#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT) - -static inline void tosakbd_discharge_all(void) -{ - /* STROBE All HiZ */ - GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT; - GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT; - GPCR2 = TOSA_GPIO_LOW_STROBE_BIT; - GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT; -} - -static inline void tosakbd_activate_all(void) -{ - /* STROBE ALL -> High */ - GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT; - GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT; - GPSR2 = TOSA_GPIO_LOW_STROBE_BIT; - GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT; - - udelay(KB_DISCHARGE_DELAY); - - /* STATE CLEAR */ - GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT; -} - -static inline void tosakbd_activate_col(int col) -{ - if (col <= 5) { - /* STROBE col -> High, not col -> HiZ */ - GPSR1 = TOSA_GPIO_STROBE_BIT(col); - GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); - } else { - /* STROBE col -> High, not col -> HiZ */ - GPSR2 = TOSA_GPIO_STROBE_BIT(col); - GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); - } -} - -static inline void tosakbd_reset_col(int col) -{ - if (col <= 5) { - /* STROBE col -> Low */ - GPCR1 = TOSA_GPIO_STROBE_BIT(col); - /* STROBE col -> out, not col -> HiZ */ - GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); - } else { - /* STROBE col -> Low */ - GPCR2 = TOSA_GPIO_STROBE_BIT(col); - /* STROBE col -> out, not col -> HiZ */ - GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); - } -} -/* - * The tosa 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 tosakbd_scankeyboard(struct platform_device *dev) -{ - struct tosakbd *tosakbd = platform_get_drvdata(dev); - unsigned int row, col, rowd; - unsigned long flags; - unsigned int num_pressed = 0; - - spin_lock_irqsave(&tosakbd->lock, flags); - - if (tosakbd->suspended) - goto out; - - for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) { - /* - * Discharge the output driver capacitatance - * in the keyboard matrix. (Yes it is significant..) - */ - tosakbd_discharge_all(); - udelay(KB_DISCHARGE_DELAY); - - tosakbd_activate_col(col); - udelay(KB_ACTIVATE_DELAY); - - rowd = GET_ROWS_STATUS(col); - - for (row = 0; row < TOSA_KEY_SENSE_NUM; row++) { - unsigned int scancode, pressed; - scancode = SCANCODE(row, col); - pressed = rowd & KB_ROWMASK(row); - - if (pressed && !tosakbd->keycode[scancode]) - dev_warn(&dev->dev, - "unhandled scancode: 0x%02x\n", - scancode); - - input_report_key(tosakbd->input, - tosakbd->keycode[scancode], - pressed); - if (pressed) - num_pressed++; - } - - tosakbd_reset_col(col); - } - - tosakbd_activate_all(); - - input_sync(tosakbd->input); - - /* if any keys are pressed, enable the timer */ - if (num_pressed) - mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL); - - out: - spin_unlock_irqrestore(&tosakbd->lock, flags); -} - -/* - * tosa keyboard interrupt handler. - */ -static irqreturn_t tosakbd_interrupt(int irq, void *__dev) -{ - struct platform_device *dev = __dev; - struct tosakbd *tosakbd = platform_get_drvdata(dev); - - if (!timer_pending(&tosakbd->timer)) { - /** wait chattering delay **/ - udelay(20); - tosakbd_scankeyboard(dev); - } - - return IRQ_HANDLED; -} - -/* - * tosa timer checking for released keys - */ -static void tosakbd_timer_callback(unsigned long __dev) -{ - struct platform_device *dev = (struct platform_device *)__dev; - - tosakbd_scankeyboard(dev); -} - -#ifdef CONFIG_PM -static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) -{ - struct tosakbd *tosakbd = platform_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&tosakbd->lock, flags); - tosakbd->suspended = true; - spin_unlock_irqrestore(&tosakbd->lock, flags); - - del_timer_sync(&tosakbd->timer); - - return 0; -} - -static int tosakbd_resume(struct platform_device *dev) -{ - struct tosakbd *tosakbd = platform_get_drvdata(dev); - - tosakbd->suspended = false; - tosakbd_scankeyboard(dev); - - return 0; -} -#else -#define tosakbd_suspend NULL -#define tosakbd_resume NULL -#endif - -static int __devinit tosakbd_probe(struct platform_device *pdev) { - - int i; - struct tosakbd *tosakbd; - struct input_dev *input_dev; - int error; - - tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL); - if (!tosakbd) - return -ENOMEM; - - input_dev = input_allocate_device(); - if (!input_dev) { - kfree(tosakbd); - return -ENOMEM; - } - - platform_set_drvdata(pdev, tosakbd); - - spin_lock_init(&tosakbd->lock); - - /* Init Keyboard rescan timer */ - init_timer(&tosakbd->timer); - tosakbd->timer.function = tosakbd_timer_callback; - tosakbd->timer.data = (unsigned long) pdev; - - tosakbd->input = input_dev; - - input_set_drvdata(input_dev, tosakbd); - input_dev->name = "Tosa Keyboard"; - input_dev->phys = "tosakbd/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; - - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); - input_dev->keycode = tosakbd->keycode; - input_dev->keycodesize = sizeof(tosakbd->keycode[0]); - input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); - - memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode)); - - for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) - __set_bit(tosakbd->keycode[i], input_dev->keybit); - __clear_bit(KEY_RESERVED, input_dev->keybit); - - /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ - for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { - int gpio = TOSA_GPIO_KEY_SENSE(i); - int irq; - error = gpio_request(gpio, "tosakbd"); - if (error < 0) { - printk(KERN_ERR "tosakbd: failed to request GPIO %d, " - " error %d\n", gpio, error); - goto fail; - } - - error = gpio_direction_input(TOSA_GPIO_KEY_SENSE(i)); - if (error < 0) { - printk(KERN_ERR "tosakbd: failed to configure input" - " direction for GPIO %d, error %d\n", - gpio, error); - gpio_free(gpio); - goto fail; - } - - irq = gpio_to_irq(gpio); - if (irq < 0) { - error = irq; - printk(KERN_ERR "gpio-keys: Unable to get irq number" - " for GPIO %d, error %d\n", - gpio, error); - gpio_free(gpio); - goto fail; - } - - error = request_irq(irq, tosakbd_interrupt, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "tosakbd", pdev); - - if (error) { - printk("tosakbd: Can't get IRQ: %d: error %d!\n", - irq, error); - gpio_free(gpio); - goto fail; - } - } - - /* Set Strobe lines as outputs - set high */ - for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) { - int gpio = TOSA_GPIO_KEY_STROBE(i); - error = gpio_request(gpio, "tosakbd"); - if (error < 0) { - printk(KERN_ERR "tosakbd: failed to request GPIO %d, " - " error %d\n", gpio, error); - goto fail2; - } - - error = gpio_direction_output(gpio, 1); - if (error < 0) { - printk(KERN_ERR "tosakbd: failed to configure input" - " direction for GPIO %d, error %d\n", - gpio, error); - gpio_free(gpio); - goto fail2; - } - - } - - error = input_register_device(input_dev); - if (error) { - printk(KERN_ERR "tosakbd: Unable to register input device, " - "error: %d\n", error); - goto fail2; - } - - printk(KERN_INFO "input: Tosa Keyboard Registered\n"); - - return 0; - -fail2: - while (--i >= 0) - gpio_free(TOSA_GPIO_KEY_STROBE(i)); - - i = TOSA_KEY_SENSE_NUM; -fail: - while (--i >= 0) { - free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), pdev); - gpio_free(TOSA_GPIO_KEY_SENSE(i)); - } - - platform_set_drvdata(pdev, NULL); - input_free_device(input_dev); - kfree(tosakbd); - - return error; -} - -static int __devexit tosakbd_remove(struct platform_device *dev) -{ - int i; - struct tosakbd *tosakbd = platform_get_drvdata(dev); - - for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) - gpio_free(TOSA_GPIO_KEY_STROBE(i)); - - for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { - free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), dev); - gpio_free(TOSA_GPIO_KEY_SENSE(i)); - } - - del_timer_sync(&tosakbd->timer); - - input_unregister_device(tosakbd->input); - - kfree(tosakbd); - - return 0; -} - -static struct platform_driver tosakbd_driver = { - .probe = tosakbd_probe, - .remove = __devexit_p(tosakbd_remove), - .suspend = tosakbd_suspend, - .resume = tosakbd_resume, - .driver = { - .name = "tosa-keyboard", - .owner = THIS_MODULE, - }, -}; - -static int __devinit tosakbd_init(void) -{ - return platform_driver_register(&tosakbd_driver); -} - -static void __exit tosakbd_exit(void) -{ - platform_driver_unregister(&tosakbd_driver); -} - -module_init(tosakbd_init); -module_exit(tosakbd_exit); - -MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); -MODULE_DESCRIPTION("Tosa Keyboard Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tosa-keyboard"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 21d6184efa9..c5a11700a1b 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -27,12 +27,12 @@ #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> +#include <linux/of.h> /* * The TWL4030 family chips include a keypad controller that supports @@ -50,12 +50,17 @@ */ #define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */ #define TWL4030_MAX_COLS 8 -#define TWL4030_ROW_SHIFT 3 -#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * TWL4030_MAX_COLS) +/* + * 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]; + bool autorepeat; unsigned n_rows; unsigned n_cols; unsigned irq; @@ -181,7 +186,7 @@ static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state) return ret; } -static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state) +static bool twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state) { int i; u16 check = 0; @@ -190,12 +195,12 @@ static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state) u16 col = key_state[i]; if ((col & check) && hweight16(col) > 1) - return 1; + return true; check |= col; } - return 0; + return false; } static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all) @@ -224,7 +229,8 @@ static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all) if (!changed) continue; - for (col = 0; col < kp->n_cols; col++) { + /* Extra column handles "all gnd" rows */ + for (col = 0; col < kp->n_cols + 1; col++) { int code; if (!(changed & (1 << col))) @@ -266,7 +272,7 @@ static irqreturn_t do_kp_irq(int irq, void *_kp) return IRQ_HANDLED; } -static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) +static int twl4030_kp_program(struct twl4030_keypad *kp) { u8 reg; int i; @@ -296,7 +302,7 @@ static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0) return -EIO; - /* Set timeout period to 100 ms */ + /* 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; @@ -323,71 +329,91 @@ static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) * Registers keypad device with input subsystem * and configures TWL4030 keypad registers */ -static int __devinit twl4030_kp_probe(struct platform_device *pdev) +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 = pdata->keymap_data; + struct twl4030_keypad_data *pdata = dev_get_platdata(&pdev->dev); + const struct matrix_keymap_data *keymap_data = NULL; struct twl4030_keypad *kp; struct input_dev *input; u8 reg; int error; - if (!pdata || !pdata->rows || !pdata->cols || - pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) { - dev_err(&pdev->dev, "Invalid platform_data\n"); - return -EINVAL; - } - - kp = kzalloc(sizeof(*kp), GFP_KERNEL); - input = input_allocate_device(); - if (!kp || !input) { - error = -ENOMEM; - goto err1; - } + kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; - /* Get the debug Device */ - kp->dbg_dev = &pdev->dev; - kp->input = input; + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; - kp->n_rows = pdata->rows; - kp->n_cols = pdata->cols; - kp->irq = platform_get_irq(pdev, 0); + /* get the debug device */ + kp->dbg_dev = &pdev->dev; + kp->input = input; /* setup input device */ - __set_bit(EV_KEY, input->evbit); - - /* Enable auto repeat feature of Linux input subsystem */ - if (pdata->rep) - __set_bit(EV_REP, input->evbit); - - input_set_capability(input, EV_MSC, MSC_SCAN); - 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; - input->keycode = kp->keymap; - input->keycodesize = sizeof(kp->keymap[0]); - input->keycodemax = ARRAY_SIZE(kp->keymap); + if (pdata) { + if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { + dev_err(&pdev->dev, "Missing platform_data\n"); + return -EINVAL; + } + + kp->n_rows = pdata->rows; + kp->n_cols = pdata->cols; + kp->autorepeat = pdata->rep; + keymap_data = pdata->keymap_data; + } else { + error = matrix_keypad_parse_of_params(&pdev->dev, &kp->n_rows, + &kp->n_cols); + if (error) + return error; + + kp->autorepeat = true; + } + + if (kp->n_rows > TWL4030_MAX_ROWS || kp->n_cols > TWL4030_MAX_COLS) { + dev_err(&pdev->dev, + "Invalid rows/cols amount specified in platform/devicetree data\n"); + return -EINVAL; + } + + kp->irq = platform_get_irq(pdev, 0); + if (!kp->irq) { + dev_err(&pdev->dev, "no keyboard irq assigned\n"); + return -EINVAL; + } + + 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"); + return error; + } - matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT, - input->keycode, input->keybit); + input_set_capability(input, EV_MSC, MSC_SCAN); + /* Enable auto repeat feature of Linux input subsystem */ + if (kp->autorepeat) + __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; + return error; } error = twl4030_kp_program(kp); if (error) - goto err2; + return error; /* * This ISR will always execute in kernel thread context because of @@ -395,49 +421,33 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) * * 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); + error = devm_request_threaded_irq(&pdev->dev, 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 err3; + dev_info(kp->dbg_dev, "request_irq failed for irq no=%d: %d\n", + kp->irq, error); + return error; } /* 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 err4; + /* mask all events - we don't care about the result */ + (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); + return -EIO; } platform_set_drvdata(pdev, kp); return 0; - -err4: - /* mask all events - we don't care about the result */ - (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); -err3: - free_irq(kp->irq, NULL); -err2: - input_unregister_device(input); - input = NULL; -err1: - input_free_device(input); - kfree(kp); - return error; } -static int __devexit 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; -} +#ifdef CONFIG_OF +static const struct of_device_id twl4030_keypad_dt_match_table[] = { + { .compatible = "ti,twl4030-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, twl4030_keypad_dt_match_table); +#endif /* * NOTE: twl4030 are multi-function devices connected via I2C. @@ -447,27 +457,15 @@ static int __devexit twl4030_kp_remove(struct platform_device *pdev) static struct platform_driver twl4030_kp_driver = { .probe = twl4030_kp_probe, - .remove = __devexit_p(twl4030_kp_remove), .driver = { .name = "twl4030_keypad", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl4030_keypad_dt_match_table), }, }; - -static int __init twl4030_kp_init(void) -{ - return platform_driver_register(&twl4030_kp_driver); -} -module_init(twl4030_kp_init); - -static void __exit twl4030_kp_exit(void) -{ - platform_driver_unregister(&twl4030_kp_driver); -} -module_exit(twl4030_kp_exit); +module_platform_driver(twl4030_kp_driver); MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TWL4030 Keypad Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_keypad"); - diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index 6032def0370..e8b9d94daae 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/device.h> @@ -19,8 +18,9 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/slab.h> -#include <mach/w90p910_keypad.h> +#include <linux/platform_data/keypad-w90p910.h> /* Keypad Interface Control Registers */ #define KPI_CONF 0x00 @@ -41,7 +41,8 @@ #define KGET_RAW(n) (((n) & KEY0R) >> 3) #define KGET_COLUMN(n) ((n) & KEY0C) -#define W90P910_MAX_KEY_NUM (8 * 8) +#define W90P910_NUM_ROWS 8 +#define W90P910_NUM_COLS 8 #define W90P910_ROW_SHIFT 3 struct w90p910_keypad { @@ -50,7 +51,7 @@ struct w90p910_keypad { struct input_dev *input_dev; void __iomem *mmio_base; int irq; - unsigned short keymap[W90P910_MAX_KEY_NUM]; + unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS]; }; static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, @@ -116,10 +117,10 @@ static void w90p910_keypad_close(struct input_dev *dev) clk_disable(keypad->clk); } -static int __devinit w90p910_keypad_probe(struct platform_device *pdev) +static int w90p910_keypad_probe(struct platform_device *pdev) { const struct w90p910_keypad_platform_data *pdata = - pdev->dev.platform_data; + dev_get_platdata(&pdev->dev); const struct matrix_keymap_data *keymap_data; struct w90p910_keypad *keypad; struct input_dev *input_dev; @@ -189,25 +190,25 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev) input_dev->close = w90p910_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->keymap; - input_dev->keycodesize = sizeof(keypad->keymap[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keymap); - - 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); - - matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT, - input_dev->keycode, input_dev->keybit); + 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, - IRQF_DISABLED, pdev->name, keypad); + 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) { @@ -219,7 +220,7 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(irq, pdev); + free_irq(irq, keypad); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -232,12 +233,12 @@ failed_free: return error; } -static int __devexit w90p910_keypad_remove(struct platform_device *pdev) +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); + free_irq(keypad->irq, keypad); clk_put(keypad->clk); @@ -247,7 +248,6 @@ static int __devexit w90p910_keypad_remove(struct platform_device *pdev) 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; @@ -255,25 +255,13 @@ static int __devexit w90p910_keypad_remove(struct platform_device *pdev) static struct platform_driver w90p910_keypad_driver = { .probe = w90p910_keypad_probe, - .remove = __devexit_p(w90p910_keypad_remove), + .remove = w90p910_keypad_remove, .driver = { - .name = "nuc900-keypad", + .name = "nuc900-kpi", .owner = THIS_MODULE, }, }; - -static int __init w90p910_keypad_init(void) -{ - return platform_driver_register(&w90p910_keypad_driver); -} - -static void __exit w90p910_keypad_exit(void) -{ - platform_driver_unregister(&w90p910_keypad_driver); -} - -module_init(w90p910_keypad_init); -module_exit(w90p910_keypad_exit); +module_platform_driver(w90p910_keypad_driver); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); MODULE_DESCRIPTION("w90p910 keypad driver"); diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c index 37b01d777a4..7c2325bd740 100644 --- a/drivers/input/keyboard/xtkbd.c +++ b/drivers/input/keyboard/xtkbd.c @@ -29,7 +29,6 @@ #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" @@ -169,15 +168,4 @@ static struct serio_driver xtkbd_drv = { .disconnect = xtkbd_disconnect, }; -static int __init xtkbd_init(void) -{ - return serio_register_driver(&xtkbd_drv); -} - -static void __exit xtkbd_exit(void) -{ - serio_unregister_driver(&xtkbd_drv); -} - -module_init(xtkbd_init); -module_exit(xtkbd_exit); +module_serio_driver(xtkbd_drv); |
