blob: ef3a2b54d22630dbdedcbec1fe4ec889f03170ee [file] [log] [blame]
/* Quanta I2C Keyboard Driver
*
* Copyright (C) 2009 Quanta Computer Inc.
* Copyright (c) 2010, The Linux Foundation. All rights reserved.
* Author: Hsin Wu <hsin.wu@quantatw.com>
* Author: Austin Lai <austin.lai@quantatw.com>
*
* 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.
*
*/
/*
*
* The Driver with I/O communications via the I2C Interface for ON2 of AP BU.
* And it is only working on the nuvoTon WPCE775x Embedded Controller.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/keyboard.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/input/qci_kbd.h>
/* Keyboard special scancode */
#define RC_KEY_FN 0x70
#define RC_KEY_BREAK 0x80
#define KEY_ACK_FA 0xFA
#define SCAN_EMUL0 0xE0
#define SCAN_EMUL1 0xE1
#define SCAN_PAUSE1 0x1D
#define SCAN_PAUSE2 0x45
#define SCAN_LIDSW_OPEN 0x70
#define SCAN_LIDSW_CLOSE 0x71
/* Keyboard keycodes */
#define NOKEY KEY_RESERVED
#define KEY_LEFTWIN KEY_LEFTMETA
#define KEY_RIGHTWIN KEY_RIGHTMETA
#define KEY_APPS KEY_COMPOSE
#define KEY_PRINTSCR KEY_SYSRQ
#define KEYBOARD_ID_NAME "qci-i2ckbd"
#define KEYBOARD_NAME "Quanta Keyboard"
#define KEYBOARD_DEVICE "/i2c/input0"
#define KEYBOARD_CMD_ENABLE 0xF4
#define KEYBOARD_CMD_SET_LED 0xED
/*-----------------------------------------------------------------------------
* Keyboard scancode to linux keycode translation table
*---------------------------------------------------------------------------*/
static const unsigned char on2_keycode[256] = {
[0] = NOKEY,
[1] = NOKEY,
[2] = NOKEY,
[3] = KEY_5,
[4] = KEY_7,
[5] = KEY_9,
[6] = KEY_MINUS,
[7] = NOKEY,
[8] = NOKEY,
[9] = NOKEY,
[10] = NOKEY,
[11] = KEY_LEFTBRACE,
[12] = KEY_F10,
[13] = KEY_INSERT,
[14] = KEY_F11,
[15] = KEY_ESC,
[16] = NOKEY,
[17] = NOKEY,
[18] = NOKEY,
[19] = KEY_4,
[20] = KEY_6,
[21] = KEY_8,
[22] = KEY_0,
[23] = KEY_EQUAL,
[24] = NOKEY,
[25] = NOKEY,
[26] = NOKEY,
[27] = KEY_P,
[28] = KEY_F9,
[29] = KEY_DELETE,
[30] = KEY_F12,
[31] = KEY_GRAVE,
[32] = KEY_W,
[33] = NOKEY,
[34] = NOKEY,
[35] = KEY_R,
[36] = KEY_T,
[37] = KEY_U,
[38] = KEY_O,
[39] = KEY_RIGHTBRACE,
[40] = NOKEY,
[41] = NOKEY,
[42] = NOKEY,
[43] = KEY_APOSTROPHE,
[44] = KEY_BACKSPACE,
[45] = NOKEY,
[46] = KEY_F8,
[47] = KEY_F5,
[48] = KEY_S,
[49] = NOKEY,
[50] = NOKEY,
[51] = KEY_E,
[52] = KEY_H,
[53] = KEY_Y,
[54] = KEY_I,
[55] = KEY_ENTER,
[56] = NOKEY,
[57] = NOKEY,
[58] = NOKEY,
[59] = KEY_SEMICOLON,
[60] = KEY_3,
[61] = KEY_PAGEUP,
[62] = KEY_Q,
[63] = KEY_TAB,
[64] = KEY_A,
[65] = NOKEY,
[66] = NOKEY,
[67] = KEY_F,
[68] = KEY_G,
[69] = KEY_J,
[70] = KEY_L,
[71] = NOKEY,
[72] = KEY_RIGHTSHIFT,
[73] = NOKEY,
[74] = NOKEY,
[75] = KEY_SLASH,
[76] = KEY_2,
[77] = KEY_PAGEDOWN,
[78] = KEY_F4,
[79] = KEY_F1,
[80] = KEY_Z,
[81] = NOKEY,
[82] = NOKEY,
[83] = KEY_D,
[84] = KEY_V,
[85] = KEY_N,
[86] = KEY_K,
[87] = NOKEY,
[88] = KEY_LEFTSHIFT,
[89] = KEY_RIGHTCTRL,
[90] = NOKEY,
[91] = KEY_DOT,
[92] = KEY_UP,
[93] = KEY_RIGHT,
[94] = KEY_F3,
[95] = KEY_F2,
[96] = NOKEY,
[97] = NOKEY,
[98] = KEY_RIGHTALT,
[99] = KEY_X,
[100] = KEY_C,
[101] = KEY_B,
[102] = KEY_COMMA,
[103] = NOKEY,
[104] = NOKEY,
[105] = NOKEY,
[106] = NOKEY,
[107] = NOKEY,
[108] = KEY_PRINTSCR,
[109] = KEY_DOWN,
[110] = KEY_1,
[111] = KEY_CAPSLOCK,
[112] = KEY_F24,
[113] = KEY_HOME,
[114] = KEY_LEFTALT,
[115] = NOKEY,
[116] = KEY_SPACE,
[117] = KEY_BACKSLASH,
[118] = KEY_M,
[119] = KEY_COMPOSE,
[120] = NOKEY,
[121] = KEY_LEFTCTRL,
[122] = NOKEY,
[123] = NOKEY,
[124] = KEY_PAUSE,
[125] = KEY_LEFT,
[126] = KEY_F7,
[127] = KEY_F6,
[128] = NOKEY,
[129] = NOKEY,
[130] = NOKEY,
[131] = NOKEY,
[132] = NOKEY,
[133] = NOKEY,
[134] = NOKEY,
[135] = NOKEY,
[136] = NOKEY,
[137] = NOKEY,
[138] = NOKEY,
[139] = NOKEY,
[140] = NOKEY,
[141] = NOKEY,
[142] = NOKEY,
[143] = NOKEY,
[144] = NOKEY,
[145] = NOKEY,
[146] = NOKEY,
[147] = NOKEY,
[148] = NOKEY,
[149] = NOKEY,
[150] = NOKEY,
[151] = NOKEY,
[152] = NOKEY,
[153] = NOKEY,
[154] = NOKEY,
[155] = NOKEY,
[156] = NOKEY,
[157] = NOKEY,
[158] = NOKEY,
[159] = NOKEY,
[160] = NOKEY,
[161] = NOKEY,
[162] = NOKEY,
[163] = NOKEY,
[164] = NOKEY,
[165] = NOKEY,
[166] = NOKEY,
[167] = NOKEY,
[168] = NOKEY,
[169] = NOKEY,
[170] = NOKEY,
[171] = NOKEY,
[172] = NOKEY,
[173] = NOKEY,
[174] = NOKEY,
[175] = NOKEY,
[176] = NOKEY,
[177] = NOKEY,
[178] = NOKEY,
[179] = NOKEY,
[180] = NOKEY,
[181] = NOKEY,
[182] = NOKEY,
[183] = NOKEY,
[184] = NOKEY,
[185] = NOKEY,
[186] = NOKEY,
[187] = NOKEY,
[188] = NOKEY,
[189] = KEY_HOME,
[190] = NOKEY,
[191] = NOKEY,
[192] = NOKEY,
[193] = NOKEY,
[194] = NOKEY,
[195] = NOKEY,
[196] = NOKEY,
[197] = NOKEY,
[198] = NOKEY,
[199] = NOKEY,
[200] = NOKEY,
[201] = NOKEY,
[202] = NOKEY,
[203] = NOKEY,
[204] = NOKEY,
[205] = KEY_END,
[206] = NOKEY,
[207] = NOKEY,
[208] = NOKEY,
[209] = NOKEY,
[210] = NOKEY,
[211] = NOKEY,
[212] = NOKEY,
[213] = NOKEY,
[214] = NOKEY,
[215] = NOKEY,
[216] = NOKEY,
[217] = NOKEY,
[218] = NOKEY,
[219] = NOKEY,
[220] = KEY_VOLUMEUP,
[221] = KEY_BRIGHTNESSUP,
[222] = NOKEY,
[223] = NOKEY,
[224] = NOKEY,
[225] = NOKEY,
[226] = NOKEY,
[227] = NOKEY,
[228] = NOKEY,
[229] = NOKEY,
[230] = NOKEY,
[231] = NOKEY,
[232] = NOKEY,
[233] = NOKEY,
[234] = NOKEY,
[235] = NOKEY,
[236] = NOKEY,
[237] = KEY_VOLUMEDOWN,
[238] = NOKEY,
[239] = NOKEY,
[240] = NOKEY,
[241] = NOKEY,
[242] = NOKEY,
[243] = NOKEY,
[244] = NOKEY,
[245] = NOKEY,
[246] = NOKEY,
[247] = NOKEY,
[248] = NOKEY,
[249] = NOKEY,
[250] = NOKEY,
[251] = NOKEY,
[252] = NOKEY,
[253] = KEY_BRIGHTNESSDOWN,
[254] = NOKEY,
[255] = NOKEY,
};
static const u8 emul0_map[128] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 0, 0,
113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0,
115, 0, 0, 0, 0, 98, 0, 99, 100, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 102, 103, 104, 0, 105, 0, 106, 0, 107,
108, 109, 110, 111, 0, 0, 0, 0, 0, 0, 0, 139, 0, 150, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/*-----------------------------------------------------------------------------
* Global variables
*---------------------------------------------------------------------------*/
struct input_dev *g_qci_keyboard_dev;
/* General structure to hold the driver data */
struct i2ckbd_drv_data {
struct i2c_client *ki2c_client;
struct work_struct work;
struct input_dev *qcikbd_dev;
struct mutex kb_mutex;
unsigned int qcikbd_gpio; /* GPIO used for interrupt */
unsigned int qcikbd_irq;
unsigned int key_down;
unsigned int escape;
unsigned int pause_seq;
unsigned int fn;
unsigned char led_status;
bool standard_scancodes;
bool kb_leds;
bool event_led;
bool emul0;
bool emul1;
bool pause1;
};
#ifdef CONFIG_PM
static int qcikbd_suspend(struct device *dev)
{
struct i2ckbd_drv_data *context = input_get_drvdata(g_qci_keyboard_dev);
enable_irq_wake(context->qcikbd_irq);
return 0;
}
static int qcikbd_resume(struct device *dev)
{
struct i2ckbd_drv_data *context = input_get_drvdata(g_qci_keyboard_dev);
struct i2c_client *ikbdclient = context->ki2c_client;
disable_irq_wake(context->qcikbd_irq);
/* consume any keypress generated while suspended */
i2c_smbus_read_byte(ikbdclient);
return 0;
}
#endif
static int qcikbd_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int qcikbd_remove(struct i2c_client *kbd);
static const struct i2c_device_id qcikbd_idtable[] = {
{ KEYBOARD_ID_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, qcikbd_idtable);
#ifdef CONFIG_PM
static struct dev_pm_ops qcikbd_pm_ops = {
.suspend = qcikbd_suspend,
.resume = qcikbd_resume,
};
#endif
static struct i2c_driver i2ckbd_driver = {
.driver = {
.owner = THIS_MODULE,
.name = KEYBOARD_ID_NAME,
#ifdef CONFIG_PM
.pm = &qcikbd_pm_ops,
#endif
},
.probe = qcikbd_probe,
.remove = qcikbd_remove,
.id_table = qcikbd_idtable,
};
/*-----------------------------------------------------------------------------
* Driver functions
*---------------------------------------------------------------------------*/
#ifdef CONFIG_KEYBOARD_QCIKBD_LID
static void process_lid(struct input_dev *ikbdev, unsigned char scancode)
{
if (scancode == SCAN_LIDSW_OPEN)
input_report_switch(ikbdev, SW_LID, 0);
else if (scancode == SCAN_LIDSW_CLOSE)
input_report_switch(ikbdev, SW_LID, 1);
else
return;
input_sync(ikbdev);
}
#endif
static irqreturn_t qcikbd_interrupt(int irq, void *dev_id)
{
struct i2ckbd_drv_data *ikbd_drv_data = dev_id;
schedule_work(&ikbd_drv_data->work);
return IRQ_HANDLED;
}
static void qcikbd_work_handler(struct work_struct *_work)
{
unsigned char scancode;
unsigned char scancode_only;
unsigned int keycode;
struct i2ckbd_drv_data *ikbd_drv_data =
container_of(_work, struct i2ckbd_drv_data, work);
struct i2c_client *ikbdclient = ikbd_drv_data->ki2c_client;
struct input_dev *ikbdev = ikbd_drv_data->qcikbd_dev;
mutex_lock(&ikbd_drv_data->kb_mutex);
if ((ikbd_drv_data->kb_leds) && (ikbd_drv_data->event_led)) {
i2c_smbus_write_byte(ikbdclient, KEYBOARD_CMD_SET_LED);
i2c_smbus_write_byte(ikbdclient, ikbd_drv_data->led_status);
ikbd_drv_data->event_led = 0;
goto work_exit;
}
scancode = i2c_smbus_read_byte(ikbdclient);
if (scancode == KEY_ACK_FA)
goto work_exit;
if (ikbd_drv_data->standard_scancodes) {
/* pause key is E1 1D 45 */
if (scancode == SCAN_EMUL1) {
ikbd_drv_data->emul1 = 1;
goto work_exit;
}
if (ikbd_drv_data->emul1) {
ikbd_drv_data->emul1 = 0;
if ((scancode & 0x7f) == SCAN_PAUSE1)
ikbd_drv_data->pause1 = 1;
goto work_exit;
}
if (ikbd_drv_data->pause1) {
ikbd_drv_data->pause1 = 0;
if ((scancode & 0x7f) == SCAN_PAUSE2) {
input_report_key(ikbdev, KEY_PAUSE,
!(scancode & 0x80));
input_sync(ikbdev);
}
goto work_exit;
}
if (scancode == SCAN_EMUL0) {
ikbd_drv_data->emul0 = 1;
goto work_exit;
}
if (ikbd_drv_data->emul0) {
ikbd_drv_data->emul0 = 0;
scancode_only = scancode & 0x7f;
#ifdef CONFIG_KEYBOARD_QCIKBD_LID
if ((scancode_only == SCAN_LIDSW_OPEN) ||
(scancode_only == SCAN_LIDSW_CLOSE)) {
process_lid(ikbdev, scancode);
goto work_exit;
}
#endif
keycode = emul0_map[scancode_only];
if (!keycode) {
dev_err(&ikbdev->dev,
"Unrecognized scancode %02x %02x\n",
SCAN_EMUL0, scancode);
goto work_exit;
}
} else {
keycode = scancode & 0x7f;
}
/* MS bit of scancode indicates direction of keypress */
ikbd_drv_data->key_down = !(scancode & 0x80);
if (keycode) {
input_event(ikbdev, EV_MSC, MSC_SCAN, scancode);
input_report_key(ikbdev, keycode,
ikbd_drv_data->key_down);
input_sync(ikbdev);
}
goto work_exit;
}
mutex_unlock(&ikbd_drv_data->kb_mutex);
if (scancode == RC_KEY_FN) {
ikbd_drv_data->fn = 0x80; /* select keycode table > 0x7F */
} else {
ikbd_drv_data->key_down = 1;
if (scancode & RC_KEY_BREAK) {
ikbd_drv_data->key_down = 0;
if ((scancode & 0x7F) == RC_KEY_FN)
ikbd_drv_data->fn = 0;
}
keycode = on2_keycode[(scancode & 0x7F) | ikbd_drv_data->fn];
if (keycode != NOKEY) {
input_report_key(ikbdev,
keycode,
ikbd_drv_data->key_down);
input_sync(ikbdev);
}
}
return;
work_exit:
mutex_unlock(&ikbd_drv_data->kb_mutex);
}
static int qcikbd_input_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
struct i2ckbd_drv_data *ikbd_drv_data = input_get_drvdata(dev);
struct input_dev *ikbdev = ikbd_drv_data->qcikbd_dev;
if (type != EV_LED)
return -EINVAL;
ikbd_drv_data->led_status =
(test_bit(LED_SCROLLL, ikbdev->led) ? 1 : 0) |
(test_bit(LED_NUML, ikbdev->led) ? 2 : 0) |
(test_bit(LED_CAPSL, ikbdev->led) ? 4 : 0);
ikbd_drv_data->event_led = 1;
schedule_work(&ikbd_drv_data->work);
return 0;
}
static int qcikbd_open(struct input_dev *dev)
{
struct i2ckbd_drv_data *ikbd_drv_data = input_get_drvdata(dev);
struct i2c_client *ikbdclient = ikbd_drv_data->ki2c_client;
/* Send F4h - enable keyboard */
i2c_smbus_write_byte(ikbdclient, KEYBOARD_CMD_ENABLE);
return 0;
}
static int qcikbd_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
int i;
struct i2ckbd_drv_data *context;
struct qci_kbd_platform_data *pdata = client->dev.platform_data;
if (!pdata) {
pr_err("[KBD] platform data not supplied\n");
return -EINVAL;
}
context = kzalloc(sizeof(struct i2ckbd_drv_data), GFP_KERNEL);
if (!context)
return -ENOMEM;
i2c_set_clientdata(client, context);
context->ki2c_client = client;
context->qcikbd_gpio = client->irq;
client->driver = &i2ckbd_driver;
INIT_WORK(&context->work, qcikbd_work_handler);
mutex_init(&context->kb_mutex);
err = gpio_request(context->qcikbd_gpio, "qci-kbd");
if (err) {
pr_err("[KBD] err gpio request\n");
goto gpio_request_fail;
}
context->qcikbd_irq = gpio_to_irq(context->qcikbd_gpio);
err = request_irq(context->qcikbd_irq,
qcikbd_interrupt,
IRQF_TRIGGER_FALLING,
KEYBOARD_ID_NAME,
context);
if (err) {
pr_err("[KBD] err unable to get IRQ\n");
goto request_irq_fail;
}
context->standard_scancodes = pdata->standard_scancodes;
context->kb_leds = pdata->kb_leds;
context->qcikbd_dev = input_allocate_device();
if (!context->qcikbd_dev) {
pr_err("[KBD]allocting memory err\n");
err = -ENOMEM;
goto allocate_fail;
}
context->qcikbd_dev->name = KEYBOARD_NAME;
context->qcikbd_dev->phys = KEYBOARD_DEVICE;
context->qcikbd_dev->id.bustype = BUS_I2C;
context->qcikbd_dev->id.vendor = 0x1050;
context->qcikbd_dev->id.product = 0x0006;
context->qcikbd_dev->id.version = 0x0004;
context->qcikbd_dev->open = qcikbd_open;
set_bit(EV_KEY, context->qcikbd_dev->evbit);
__set_bit(MSC_SCAN, context->qcikbd_dev->mscbit);
if (pdata->repeat)
set_bit(EV_REP, context->qcikbd_dev->evbit);
/* Enable all supported keys */
for (i = 1; i < ARRAY_SIZE(on2_keycode) ; i++)
set_bit(on2_keycode[i], context->qcikbd_dev->keybit);
set_bit(KEY_POWER, context->qcikbd_dev->keybit);
set_bit(KEY_END, context->qcikbd_dev->keybit);
set_bit(KEY_VOLUMEUP, context->qcikbd_dev->keybit);
set_bit(KEY_VOLUMEDOWN, context->qcikbd_dev->keybit);
set_bit(KEY_ZOOMIN, context->qcikbd_dev->keybit);
set_bit(KEY_ZOOMOUT, context->qcikbd_dev->keybit);
#ifdef CONFIG_KEYBOARD_QCIKBD_LID
set_bit(EV_SW, context->qcikbd_dev->evbit);
set_bit(SW_LID, context->qcikbd_dev->swbit);
#endif
if (context->kb_leds) {
context->qcikbd_dev->event = qcikbd_input_event;
__set_bit(EV_LED, context->qcikbd_dev->evbit);
__set_bit(LED_NUML, context->qcikbd_dev->ledbit);
__set_bit(LED_CAPSL, context->qcikbd_dev->ledbit);
__set_bit(LED_SCROLLL, context->qcikbd_dev->ledbit);
}
input_set_drvdata(context->qcikbd_dev, context);
err = input_register_device(context->qcikbd_dev);
if (err) {
pr_err("[KBD] err input register device\n");
goto register_fail;
}
g_qci_keyboard_dev = context->qcikbd_dev;
return 0;
register_fail:
input_free_device(context->qcikbd_dev);
allocate_fail:
free_irq(context->qcikbd_irq, context);
request_irq_fail:
gpio_free(context->qcikbd_gpio);
gpio_request_fail:
i2c_set_clientdata(client, NULL);
kfree(context);
return err;
}
static int qcikbd_remove(struct i2c_client *dev)
{
struct i2ckbd_drv_data *context = i2c_get_clientdata(dev);
free_irq(context->qcikbd_irq, context);
gpio_free(context->qcikbd_gpio);
input_free_device(context->qcikbd_dev);
input_unregister_device(context->qcikbd_dev);
kfree(context);
return 0;
}
static int __init qcikbd_init(void)
{
return i2c_add_driver(&i2ckbd_driver);
}
static void __exit qcikbd_exit(void)
{
i2c_del_driver(&i2ckbd_driver);
}
struct input_dev *nkbc_keypad_get_input_dev(void)
{
return g_qci_keyboard_dev;
}
EXPORT_SYMBOL(nkbc_keypad_get_input_dev);
module_init(qcikbd_init);
module_exit(qcikbd_exit);
MODULE_AUTHOR("Quanta Computer Inc.");
MODULE_DESCRIPTION("Quanta Embedded Controller I2C Keyboard Driver");
MODULE_LICENSE("GPL v2");