diff options
Diffstat (limited to 'kernel/drivers/input/keyboard')
25 files changed, 562 insertions, 124 deletions
diff --git a/kernel/drivers/input/keyboard/Kconfig b/kernel/drivers/input/keyboard/Kconfig index 106fbac7f..ddd8148d5 100644 --- a/kernel/drivers/input/keyboard/Kconfig +++ b/kernel/drivers/input/keyboard/Kconfig @@ -187,7 +187,7 @@ config KEYBOARD_EP93XX config KEYBOARD_GPIO tristate "GPIO Buttons" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help This driver implements support for buttons connected to GPIO pins of various CPUs (and some other chips). @@ -253,7 +253,7 @@ config KEYBOARD_TCA8418 config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select INPUT_MATRIXKMAP help Enable support for GPIO driven matrix keypad. @@ -367,6 +367,7 @@ config KEYBOARD_MAPLE config KEYBOARD_MAX7359 tristate "Maxim MAX7359 Key Switch Controller" + select INPUT_MATRIXKMAP depends on I2C help If you say yes here you get support for the Maxim MAX7359 Key @@ -400,6 +401,17 @@ config KEYBOARD_MPR121 To compile this driver as a module, choose M here: the module will be called mpr121_touchkey. +config KEYBOARD_SNVS_PWRKEY + tristate "IMX SNVS Power Key Driver" + depends on SOC_IMX6SX + depends on OF + help + This is the snvs powerkey driver for the Freescale i.MX application + processors that are newer than i.MX6 SX. + + To compile this driver as a module, choose M here; the + module will be called snvs_pwrkey. + config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC @@ -504,7 +516,7 @@ config KEYBOARD_SAMSUNG module will be called samsung-keypad. config KEYBOARD_GOLDFISH_EVENTS - depends on GOLDFISH + depends on GOLDFISH || COMPILE_TEST tristate "Generic Input Event device for Goldfish" help Say Y here to get an input event device for the Goldfish virtual diff --git a/kernel/drivers/input/keyboard/Makefile b/kernel/drivers/input/keyboard/Makefile index df28d5553..1d416ddf8 100644 --- a/kernel/drivers/input/keyboard/Makefile +++ b/kernel/drivers/input/keyboard/Makefile @@ -51,6 +51,7 @@ 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_SNVS_PWRKEY) += snvs_pwrkey.o obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o diff --git a/kernel/drivers/input/keyboard/adp5589-keys.c b/kernel/drivers/input/keyboard/adp5589-keys.c index a45267729..4d446d508 100644 --- a/kernel/drivers/input/keyboard/adp5589-keys.c +++ b/kernel/drivers/input/keyboard/adp5589-keys.c @@ -180,7 +180,7 @@ #define LOGIC2_STAT (1 << 7) /* ADP5589 only */ #define LOGIC1_STAT (1 << 6) #define LOCK_STAT (1 << 5) /* ADP5589 only */ -#define KEC 0xF +#define KEC 0x1F /* PIN_CONFIG_D Register */ #define C4_EXTEND_CFG (1 << 6) /* RESET2 */ @@ -726,7 +726,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_row_num) { + if (i % 4 == 3 || i == kpad->var->max_row_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) + (i >> 2), pull_mask); pull_mask = 0; @@ -746,7 +746,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_col_num) { + if (i % 4 == 3 || i == kpad->var->max_col_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_C) + (i >> 2), pull_mask); @@ -1097,7 +1097,6 @@ 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, diff --git a/kernel/drivers/input/keyboard/cap11xx.c b/kernel/drivers/input/keyboard/cap11xx.c index f07461a64..378db1000 100644 --- a/kernel/drivers/input/keyboard/cap11xx.c +++ b/kernel/drivers/input/keyboard/cap11xx.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/input.h> +#include <linux/leds.h> #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/i2c.h> @@ -47,6 +48,20 @@ #define CAP11XX_REG_CONFIG2 0x44 #define CAP11XX_REG_CONFIG2_ALT_POL BIT(6) #define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) +#define CAP11XX_REG_LED_POLARITY 0x73 +#define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74 + +#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90 +#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91 +#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92 +#define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93 + +#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f) +#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0) +#define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0) +#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4) +#define CAP11XX_REG_LED_DUTY_MAX_VALUE (15) + #define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X)) #define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9 #define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba @@ -56,10 +71,23 @@ #define CAP11XX_MANUFACTURER_ID 0x5d +#ifdef CONFIG_LEDS_CLASS +struct cap11xx_led { + struct cap11xx_priv *priv; + struct led_classdev cdev; + struct work_struct work; + u32 reg; + enum led_brightness new_brightness; +}; +#endif + struct cap11xx_priv { struct regmap *regmap; struct input_dev *idev; + struct cap11xx_led *leds; + int num_leds; + /* config */ u32 keycodes[]; }; @@ -67,6 +95,7 @@ struct cap11xx_priv { struct cap11xx_hw_model { u8 product_id; unsigned int num_channels; + unsigned int num_leds; }; enum { @@ -76,9 +105,9 @@ enum { }; static const struct cap11xx_hw_model cap11xx_devices[] = { - [CAP1106] = { .product_id = 0x55, .num_channels = 6 }, - [CAP1126] = { .product_id = 0x53, .num_channels = 6 }, - [CAP1188] = { .product_id = 0x50, .num_channels = 8 }, + [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 }, + [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 }, + [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 }, }; static const struct reg_default cap11xx_reg_defaults[] = { @@ -111,6 +140,7 @@ static const struct reg_default cap11xx_reg_defaults[] = { { CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 }, { CAP11XX_REG_STANDBY_THRESH, 0x40 }, { CAP11XX_REG_CONFIG2, 0x40 }, + { CAP11XX_REG_LED_POLARITY, 0x00 }, { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 }, { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 }, }; @@ -177,6 +207,12 @@ out: static int cap11xx_set_sleep(struct cap11xx_priv *priv, bool sleep) { + /* + * DLSEEP mode will turn off all LEDS, prevent this + */ + if (IS_ENABLED(CONFIG_LEDS_CLASS) && priv->num_leds) + return 0; + return regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL, CAP11XX_REG_MAIN_CONTROL_DLSEEP, sleep ? CAP11XX_REG_MAIN_CONTROL_DLSEEP : 0); @@ -196,6 +232,104 @@ static void cap11xx_input_close(struct input_dev *idev) cap11xx_set_sleep(priv, true); } +#ifdef CONFIG_LEDS_CLASS +static void cap11xx_led_work(struct work_struct *work) +{ + struct cap11xx_led *led = container_of(work, struct cap11xx_led, work); + struct cap11xx_priv *priv = led->priv; + int value = led->new_brightness; + + /* + * All LEDs share the same duty cycle as this is a HW limitation. + * Brightness levels per LED are either 0 (OFF) and 1 (ON). + */ + regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL, + BIT(led->reg), value ? BIT(led->reg) : 0); +} + +static void cap11xx_led_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev); + + if (led->new_brightness == value) + return; + + led->new_brightness = value; + schedule_work(&led->work); +} + +static int cap11xx_init_leds(struct device *dev, + struct cap11xx_priv *priv, int num_leds) +{ + struct device_node *node = dev->of_node, *child; + struct cap11xx_led *led; + int cnt = of_get_child_count(node); + int error; + + if (!num_leds || !cnt) + return 0; + + if (cnt > num_leds) + return -EINVAL; + + led = devm_kcalloc(dev, cnt, sizeof(struct cap11xx_led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + priv->leds = led; + + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0); + if (error) + return error; + + error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4, + CAP11XX_REG_LED_DUTY_MAX_MASK, + CAP11XX_REG_LED_DUTY_MAX_VALUE << + CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT); + if (error) + return error; + + for_each_child_of_node(node, child) { + u32 reg; + + led->cdev.name = + of_get_property(child, "label", NULL) ? : child->name; + led->cdev.default_trigger = + of_get_property(child, "linux,default-trigger", NULL); + led->cdev.flags = 0; + led->cdev.brightness_set = cap11xx_led_set; + led->cdev.max_brightness = 1; + led->cdev.brightness = LED_OFF; + + error = of_property_read_u32(child, "reg", ®); + if (error != 0 || reg >= num_leds) + return -EINVAL; + + led->reg = reg; + led->priv = priv; + + INIT_WORK(&led->work, cap11xx_led_work); + + error = devm_led_classdev_register(dev, &led->cdev); + if (error) + return error; + + priv->num_leds++; + led++; + } + + return 0; +} +#else +static int cap11xx_init_leds(struct device *dev, + struct cap11xx_priv *priv, int num_leds) +{ + return 0; +} +#endif + static int cap11xx_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { @@ -316,6 +450,10 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client, priv->idev->open = cap11xx_input_open; priv->idev->close = cap11xx_input_close; + error = cap11xx_init_leds(dev, priv, cap->num_leds); + if (error) + return error; + input_set_drvdata(priv->idev, priv); /* @@ -361,7 +499,6 @@ MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids); static struct i2c_driver cap11xx_i2c_driver = { .driver = { .name = "cap11xx", - .owner = THIS_MODULE, .of_match_table = cap11xx_dt_ids, }, .id_table = cap11xx_i2c_ids, diff --git a/kernel/drivers/input/keyboard/clps711x-keypad.c b/kernel/drivers/input/keyboard/clps711x-keypad.c index 27ef29f8f..b637f1af8 100644 --- a/kernel/drivers/input/keyboard/clps711x-keypad.c +++ b/kernel/drivers/input/keyboard/clps711x-keypad.c @@ -120,14 +120,9 @@ static int clps711x_keypad_probe(struct platform_device *pdev) 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; - + data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN); 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); diff --git a/kernel/drivers/input/keyboard/cros_ec_keyb.c b/kernel/drivers/input/keyboard/cros_ec_keyb.c index b50c5b8b8..b01966dc7 100644 --- a/kernel/drivers/input/keyboard/cros_ec_keyb.c +++ b/kernel/drivers/input/keyboard/cros_ec_keyb.c @@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { - int ret; - struct cros_ec_command msg = { - .command = EC_CMD_MKBP_STATE, - .insize = ckdev->cols, - }; + int ret = 0; + struct cros_ec_command *msg; - ret = cros_ec_cmd_xfer(ckdev->ec, &msg); - if (ret < 0) - return ret; + msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL); + if (!msg) + return -ENOMEM; - memcpy(kb_state, msg.indata, ckdev->cols); + msg->version = 0; + msg->command = EC_CMD_MKBP_STATE; + msg->insize = ckdev->cols; + msg->outsize = 0; - return 0; + ret = cros_ec_cmd_xfer(ckdev->ec, msg); + if (ret < 0) { + dev_err(ckdev->dev, "Error transferring EC message %d\n", ret); + goto exit; + } + + memcpy(kb_state, msg->data, ckdev->cols); +exit: + kfree(msg); + return ret; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) @@ -266,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) ckdev->dev = dev; dev_set_drvdata(&pdev->dev, ckdev); - idev->name = ec->ec_name; + idev->name = CROS_EC_DEV_NAME; idev->phys = ec->phys_name; __set_bit(EV_REP, idev->evbit); diff --git a/kernel/drivers/input/keyboard/gpio_keys.c b/kernel/drivers/input/keyboard/gpio_keys.c index ddf4045de..bef317ff7 100644 --- a/kernel/drivers/input/keyboard/gpio_keys.c +++ b/kernel/drivers/input/keyboard/gpio_keys.c @@ -239,6 +239,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, } } + if (i == ddata->pdata->nbuttons) { + error = -EINVAL; + goto out; + } + mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->pdata->nbuttons; i++) { @@ -336,8 +341,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; + int state = gpio_get_value_cansleep(button->gpio); + + if (state < 0) { + dev_err(input->dev.parent, "failed to get gpio state\n"); + return; + } + state = (state ? 1 : 0) ^ button->active_low; if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); @@ -655,7 +666,9 @@ gpio_keys_get_devtree_pdata(struct device *dev) 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); + button->wakeup = of_property_read_bool(pp, "wakeup-source") || + /* legacy name */ + of_property_read_bool(pp, "gpio-key,wakeup"); button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); diff --git a/kernel/drivers/input/keyboard/gpio_keys_polled.c b/kernel/drivers/input/keyboard/gpio_keys_polled.c index c6dc644aa..62bdb1d48 100644 --- a/kernel/drivers/input/keyboard/gpio_keys_polled.c +++ b/kernel/drivers/input/keyboard/gpio_keys_polled.c @@ -40,10 +40,36 @@ struct gpio_keys_polled_dev { struct input_polled_dev *poll_dev; struct device *dev; const struct gpio_keys_platform_data *pdata; + unsigned long rel_axis_seen[BITS_TO_LONGS(REL_CNT)]; + unsigned long abs_axis_seen[BITS_TO_LONGS(ABS_CNT)]; struct gpio_keys_button_data data[0]; }; -static void gpio_keys_polled_check_state(struct input_dev *input, +static void gpio_keys_button_event(struct input_polled_dev *dev, + struct gpio_keys_button *button, + int state) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct input_dev *input = dev->input; + unsigned int type = button->type ?: EV_KEY; + + if (type == EV_REL) { + if (state) { + input_event(input, type, button->code, button->value); + __set_bit(button->code, bdev->rel_axis_seen); + } + } else if (type == EV_ABS) { + if (state) { + input_event(input, type, button->code, button->value); + __set_bit(button->code, bdev->abs_axis_seen); + } + } else { + input_event(input, type, button->code, state); + input_sync(input); + } +} + +static void gpio_keys_polled_check_state(struct input_polled_dev *dev, struct gpio_keys_button *button, struct gpio_keys_button_data *bdata) { @@ -54,11 +80,9 @@ static void gpio_keys_polled_check_state(struct input_dev *input, else state = !!gpiod_get_value(button->gpiod); - if (state != bdata->last_state) { - unsigned int type = button->type ?: EV_KEY; + gpio_keys_button_event(dev, button, state); - input_event(input, type, button->code, state); - input_sync(input); + if (state != bdata->last_state) { bdata->count = 0; bdata->last_state = state; } @@ -71,15 +95,33 @@ static void gpio_keys_polled_poll(struct input_polled_dev *dev) struct input_dev *input = dev->input; int i; + memset(bdev->rel_axis_seen, 0, sizeof(bdev->rel_axis_seen)); + memset(bdev->abs_axis_seen, 0, sizeof(bdev->abs_axis_seen)); + for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button_data *bdata = &bdev->data[i]; - if (bdata->count < bdata->threshold) + if (bdata->count < bdata->threshold) { bdata->count++; - else - gpio_keys_polled_check_state(input, &pdata->buttons[i], + gpio_keys_button_event(dev, &pdata->buttons[i], + bdata->last_state); + } else { + gpio_keys_polled_check_state(dev, &pdata->buttons[i], bdata); + } + } + + for_each_set_bit(i, input->relbit, REL_CNT) { + if (!test_bit(i, bdev->rel_axis_seen)) + input_event(input, EV_REL, i, 0); + } + + for_each_set_bit(i, input->absbit, ABS_CNT) { + if (!test_bit(i, bdev->abs_axis_seen)) + input_event(input, EV_ABS, i, 0); } + + input_sync(input); } static void gpio_keys_polled_open(struct input_polled_dev *dev) @@ -152,7 +194,14 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct &button->type)) button->type = EV_KEY; - button->wakeup = fwnode_property_present(child, "gpio-key,wakeup"); + if (fwnode_property_read_u32(child, "linux,input-value", + (u32 *)&button->value)) + button->value = 1; + + button->wakeup = + fwnode_property_read_bool(child, "wakeup-source") || + /* legacy name */ + fwnode_property_read_bool(child, "gpio-key,wakeup"); if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) @@ -165,6 +214,25 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct return pdata; } +static void gpio_keys_polled_set_abs_params(struct input_dev *input, + const struct gpio_keys_platform_data *pdata, unsigned int code) +{ + int i, min = 0, max = 0; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + + if (button->type != EV_ABS || button->code != code) + continue; + + if (button->value < min) + min = button->value; + if (button->value > max) + max = button->value; + } + input_set_abs_params(input, code, min, max, 0, 0); +} + static const struct of_device_id gpio_keys_polled_of_match[] = { { .compatible = "gpio-keys-polled", }, { }, @@ -271,6 +339,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) pdata->poll_interval); input_set_capability(input, type, button->code); + if (type == EV_ABS) + gpio_keys_polled_set_abs_params(input, pdata, + button->code); } bdev->poll_dev = poll_dev; @@ -287,9 +358,11 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) /* report initial state of the buttons */ for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_polled_check_state(input, &pdata->buttons[i], + gpio_keys_polled_check_state(poll_dev, &pdata->buttons[i], &bdev->data[i]); + input_sync(input); + return 0; } diff --git a/kernel/drivers/input/keyboard/imx_keypad.c b/kernel/drivers/input/keyboard/imx_keypad.c index 2e855e6f3..2165f3dd3 100644 --- a/kernel/drivers/input/keyboard/imx_keypad.c +++ b/kernel/drivers/input/keyboard/imx_keypad.c @@ -5,8 +5,6 @@ * 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> @@ -506,7 +504,9 @@ static int imx_keypad_probe(struct platform_device *pdev) input_set_drvdata(input_dev, keypad); /* Ensure that the keypad will stay dormant until opened */ - clk_prepare_enable(keypad->clk); + error = clk_prepare_enable(keypad->clk); + if (error) + return error; imx_keypad_inhibit(keypad); clk_disable_unprepare(keypad->clk); diff --git a/kernel/drivers/input/keyboard/lm8333.c b/kernel/drivers/input/keyboard/lm8333.c index 0ad422b8a..c717e8f3c 100644 --- a/kernel/drivers/input/keyboard/lm8333.c +++ b/kernel/drivers/input/keyboard/lm8333.c @@ -223,7 +223,6 @@ MODULE_DEVICE_TABLE(i2c, lm8333_id); static struct i2c_driver lm8333_driver = { .driver = { .name = "lm8333", - .owner = THIS_MODULE, }, .probe = lm8333_probe, .remove = lm8333_remove, diff --git a/kernel/drivers/input/keyboard/matrix_keypad.c b/kernel/drivers/input/keyboard/matrix_keypad.c index b370a59cb..7f12b6579 100644 --- a/kernel/drivers/input/keyboard/matrix_keypad.c +++ b/kernel/drivers/input/keyboard/matrix_keypad.c @@ -425,8 +425,10 @@ matrix_keypad_parse_dt(struct device *dev) if (of_get_property(np, "linux,no-autorepeat", NULL)) pdata->no_autorepeat = true; - if (of_get_property(np, "linux,wakeup", NULL)) - pdata->wakeup = true; + + pdata->wakeup = of_property_read_bool(np, "wakeup-source") || + of_property_read_bool(np, "linux,wakeup"); /* legacy */ + if (of_get_property(np, "gpio-activelow", NULL)) pdata->active_low = true; diff --git a/kernel/drivers/input/keyboard/max7359_keypad.c b/kernel/drivers/input/keyboard/max7359_keypad.c index faa6da53e..5091133b7 100644 --- a/kernel/drivers/input/keyboard/max7359_keypad.c +++ b/kernel/drivers/input/keyboard/max7359_keypad.c @@ -84,26 +84,6 @@ static int max7359_read_reg(struct i2c_client *client, int reg) return ret; } -static void max7359_build_keycode(struct max7359_keypad *keypad, - const struct matrix_keymap_data *keymap_data) -{ - struct input_dev *input_dev = keypad->input_dev; - int i; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MAX7359_ROW_SHIFT); - unsigned short keycode = KEY_VAL(key); - - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); - } - __clear_bit(KEY_RESERVED, input_dev->keybit); -} - /* runs in an IRQ thread -- can (and will!) sleep */ static irqreturn_t max7359_interrupt(int irq, void *dev_id) { @@ -166,7 +146,6 @@ static void max7359_close(struct input_dev *dev) static void max7359_initialize(struct i2c_client *client) { max7359_write_reg(client, MAX7359_REG_CONFIG, - MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ MAX7359_CFG_KEY_RELEASE | /* Key release enable */ MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ @@ -233,7 +212,15 @@ static int max7359_probe(struct i2c_client *client, input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); - max7359_build_keycode(keypad, keymap_data); + error = matrix_keypad_build_keymap(keymap_data, NULL, + MAX7359_MAX_KEY_ROWS, + MAX7359_MAX_KEY_COLS, + keypad->keycodes, + input_dev); + if (error) { + dev_err(&client->dev, "failed to build keymap\n"); + return error; + } error = devm_request_threaded_irq(&client->dev, client->irq, NULL, max7359_interrupt, diff --git a/kernel/drivers/input/keyboard/mcs_touchkey.c b/kernel/drivers/input/keyboard/mcs_touchkey.c index 375b05ca8..31090d71a 100644 --- a/kernel/drivers/input/keyboard/mcs_touchkey.c +++ b/kernel/drivers/input/keyboard/mcs_touchkey.c @@ -265,7 +265,6 @@ 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, diff --git a/kernel/drivers/input/keyboard/mpr121_touchkey.c b/kernel/drivers/input/keyboard/mpr121_touchkey.c index 3aa2ec45b..0fd612dd7 100644 --- a/kernel/drivers/input/keyboard/mpr121_touchkey.c +++ b/kernel/drivers/input/keyboard/mpr121_touchkey.c @@ -305,7 +305,6 @@ 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, diff --git a/kernel/drivers/input/keyboard/nomadik-ske-keypad.c b/kernel/drivers/input/keyboard/nomadik-ske-keypad.c index c7d5b1666..8567ee477 100644 --- a/kernel/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/kernel/drivers/input/keyboard/nomadik-ske-keypad.c @@ -54,7 +54,7 @@ /** * struct ske_keypad - data structure used by keypad driver * @irq: irq no - * @reg_base: ske regsiters base address + * @reg_base: ske registers base address * @input: pointer to input device object * @board: keypad platform device * @keymap: matrix scan code table for keycodes diff --git a/kernel/drivers/input/keyboard/omap4-keypad.c b/kernel/drivers/input/keyboard/omap4-keypad.c index b052afec9..6639b2b85 100644 --- a/kernel/drivers/input/keyboard/omap4-keypad.c +++ b/kernel/drivers/input/keyboard/omap4-keypad.c @@ -266,7 +266,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) error = omap4_keypad_parse_dt(&pdev->dev, keypad_data); if (error) - return error; + goto err_free_keypad; res = request_mem_region(res->start, resource_size(res), pdev->name); if (!res) { diff --git a/kernel/drivers/input/keyboard/pmic8xxx-keypad.c b/kernel/drivers/input/keyboard/pmic8xxx-keypad.c index 32580afec..5c68e3f09 100644 --- a/kernel/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/kernel/drivers/input/keyboard/pmic8xxx-keypad.c @@ -507,6 +507,7 @@ static void pmic8xxx_kp_close(struct input_dev *dev) */ static int pmic8xxx_kp_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; unsigned int rows, cols; bool repeat; bool wakeup; @@ -524,10 +525,11 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) 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"); + repeat = !of_property_read_bool(np, "linux,input-no-autorepeat"); + + wakeup = of_property_read_bool(np, "wakeup-source") || + /* legacy name */ + of_property_read_bool(np, "linux,keypad-wakeup"); kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); if (!kp) diff --git a/kernel/drivers/input/keyboard/qt1070.c b/kernel/drivers/input/keyboard/qt1070.c index 52cd6e88a..5a5778729 100644 --- a/kernel/drivers/input/keyboard/qt1070.c +++ b/kernel/drivers/input/keyboard/qt1070.c @@ -277,7 +277,6 @@ 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, diff --git a/kernel/drivers/input/keyboard/qt2160.c b/kernel/drivers/input/keyboard/qt2160.c index 819b22897..43b86482d 100644 --- a/kernel/drivers/input/keyboard/qt2160.c +++ b/kernel/drivers/input/keyboard/qt2160.c @@ -497,7 +497,6 @@ MODULE_DEVICE_TABLE(i2c, qt2160_idtable); static struct i2c_driver qt2160_driver = { .driver = { .name = "qt2160", - .owner = THIS_MODULE, }, .id_table = qt2160_idtable, diff --git a/kernel/drivers/input/keyboard/samsung-keypad.c b/kernel/drivers/input/keyboard/samsung-keypad.c index 6b9fdf6cf..4e319eb9e 100644 --- a/kernel/drivers/input/keyboard/samsung-keypad.c +++ b/kernel/drivers/input/keyboard/samsung-keypad.c @@ -299,8 +299,10 @@ samsung_keypad_parse_dt(struct device *dev) 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; + pdata->wakeup = of_property_read_bool(np, "wakeup-source") || + /* legacy name */ + of_property_read_bool(np, "linux,input-wakeup"); + return pdata; } @@ -585,7 +587,7 @@ static const struct of_device_id samsung_keypad_dt_match[] = { MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); #endif -static struct platform_device_id samsung_keypad_driver_ids[] = { +static const struct platform_device_id samsung_keypad_driver_ids[] = { { .name = "samsung-keypad", .driver_data = KEYPAD_TYPE_SAMSUNG, diff --git a/kernel/drivers/input/keyboard/snvs_pwrkey.c b/kernel/drivers/input/keyboard/snvs_pwrkey.c new file mode 100644 index 000000000..9adf13a58 --- /dev/null +++ b/kernel/drivers/input/keyboard/snvs_pwrkey.c @@ -0,0 +1,226 @@ +/* + * Driver for the IMX SNVS ON/OFF Power Key + * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/input.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/of_address.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#define SNVS_LPSR_REG 0x4C /* LP Status Register */ +#define SNVS_LPCR_REG 0x38 /* LP Control Register */ +#define SNVS_HPSR_REG 0x14 +#define SNVS_HPSR_BTN BIT(6) +#define SNVS_LPSR_SPO BIT(18) +#define SNVS_LPCR_DEP_EN BIT(5) + +#define DEBOUNCE_TIME 30 +#define REPEAT_INTERVAL 60 + +struct pwrkey_drv_data { + struct regmap *snvs; + int irq; + int keycode; + int keystate; /* 1:pressed */ + int wakeup; + struct timer_list check_timer; + struct input_dev *input; +}; + +static void imx_imx_snvs_check_for_events(unsigned long data) +{ + struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data; + struct input_dev *input = pdata->input; + u32 state; + + regmap_read(pdata->snvs, SNVS_HPSR_REG, &state); + state = state & SNVS_HPSR_BTN ? 1 : 0; + + /* only report new event if status changed */ + if (state ^ pdata->keystate) { + pdata->keystate = state; + input_event(input, EV_KEY, pdata->keycode, state); + input_sync(input); + pm_relax(pdata->input->dev.parent); + } + + /* repeat check if pressed long */ + if (state) { + mod_timer(&pdata->check_timer, + jiffies + msecs_to_jiffies(REPEAT_INTERVAL)); + } +} + +static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); + u32 lp_status; + + pm_wakeup_event(pdata->input->dev.parent, 0); + + regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status); + if (lp_status & SNVS_LPSR_SPO) + mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); + + /* clear SPO status */ + regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO); + + return IRQ_HANDLED; +} + +static void imx_snvs_pwrkey_act(void *pdata) +{ + struct pwrkey_drv_data *pd = pdata; + + del_timer_sync(&pd->check_timer); +} + +static int imx_snvs_pwrkey_probe(struct platform_device *pdev) +{ + struct pwrkey_drv_data *pdata = NULL; + struct input_dev *input = NULL; + struct device_node *np; + int error; + + /* Get SNVS register Page */ + np = pdev->dev.of_node; + if (!np) + return -ENODEV; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap"); + if (!pdata->snvs) { + dev_err(&pdev->dev, "Can't get snvs syscon\n"); + return -ENODEV; + } + + if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) { + pdata->keycode = KEY_POWER; + dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n"); + } + + pdata->wakeup = of_property_read_bool(np, "wakeup-source"); + + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) { + dev_err(&pdev->dev, "no irq defined in platform data\n"); + return -EINVAL; + } + + regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN); + + /* clear the unexpected interrupt before driver ready */ + regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO); + + setup_timer(&pdata->check_timer, + imx_imx_snvs_check_for_events, (unsigned long) pdata); + + input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + input->name = pdev->name; + input->phys = "snvs-pwrkey/input0"; + input->id.bustype = BUS_HOST; + + input_set_capability(input, EV_KEY, pdata->keycode); + + /* input customer action to cancel release timer */ + error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata); + if (error) { + dev_err(&pdev->dev, "failed to register remove action\n"); + return error; + } + + error = devm_request_irq(&pdev->dev, pdata->irq, + imx_snvs_pwrkey_interrupt, + 0, pdev->name, pdev); + + if (error) { + dev_err(&pdev->dev, "interrupt not available.\n"); + return error; + } + + error = input_register_device(input); + if (error < 0) { + dev_err(&pdev->dev, "failed to register input device\n"); + input_free_device(input); + return error; + } + + pdata->input = input; + platform_set_drvdata(pdev, pdata); + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; +} + +static int imx_snvs_pwrkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(pdata->irq); + + return 0; +} + +static int imx_snvs_pwrkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(pdata->irq); + + return 0; +} + +static const struct of_device_id imx_snvs_pwrkey_ids[] = { + { .compatible = "fsl,sec-v4.0-pwrkey" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids); + +static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend, + imx_snvs_pwrkey_resume); + +static struct platform_driver imx_snvs_pwrkey_driver = { + .driver = { + .name = "snvs_pwrkey", + .pm = &imx_snvs_pwrkey_pm_ops, + .of_match_table = imx_snvs_pwrkey_ids, + }, + .probe = imx_snvs_pwrkey_probe, +}; +module_platform_driver(imx_snvs_pwrkey_driver); + +MODULE_AUTHOR("Freescale Semiconductor"); +MODULE_DESCRIPTION("i.MX snvs power key Driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/input/keyboard/spear-keyboard.c b/kernel/drivers/input/keyboard/spear-keyboard.c index f42a543db..623d45176 100644 --- a/kernel/drivers/input/keyboard/spear-keyboard.c +++ b/kernel/drivers/input/keyboard/spear-keyboard.c @@ -3,7 +3,7 @@ * Based on omap-keypad driver * * Copyright (C) 2010 ST Microelectronics - * Rajeev Kumar<rajeev-dlh.kumar@st.com> + * Rajeev Kumar <rajeevkumar.linux@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any diff --git a/kernel/drivers/input/keyboard/tc3589x-keypad.c b/kernel/drivers/input/keyboard/tc3589x-keypad.c index 31c606a4d..e92dfd888 100644 --- a/kernel/drivers/input/keyboard/tc3589x-keypad.c +++ b/kernel/drivers/input/keyboard/tc3589x-keypad.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/mfd/tc3589x.h> +#include <linux/device.h> /* Maximum supported keypad matrix row/columns size */ #define TC3589x_MAX_KPROW 8 @@ -352,7 +353,10 @@ tc3589x_keypad_of_probe(struct device *dev) } plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat"); - plat->enable_wakeup = of_property_read_bool(np, "linux,wakeup"); + + plat->enable_wakeup = of_property_read_bool(np, "wakeup-source") || + /* legacy name */ + of_property_read_bool(np, "linux,wakeup"); /* The custom delay format is ms/16 */ of_property_read_u32(np, "debounce-delay-ms", &debounce_ms); @@ -386,12 +390,15 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) 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 = devm_kzalloc(&pdev->dev, sizeof(struct tc_keypad), + GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; } keypad->board = plat; @@ -410,7 +417,7 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) NULL, input); if (error) { dev_err(&pdev->dev, "Failed to build keymap\n"); - goto err_free_mem; + return error; } keypad->keymap = input->keycode; @@ -421,20 +428,23 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) input_set_drvdata(input, keypad); - error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq, - plat->irqtype | IRQF_ONESHOT, - "tc3589x-keypad", keypad); - if (error < 0) { + tc3589x_keypad_disable(keypad); + + error = devm_request_threaded_irq(&pdev->dev, irq, + NULL, tc3589x_keypad_irq, + plat->irqtype | IRQF_ONESHOT, + "tc3589x-keypad", keypad); + if (error) { dev_err(&pdev->dev, "Could not allocate irq %d,error %d\n", irq, error); - goto err_free_mem; + return error; } error = input_register_device(input); if (error) { dev_err(&pdev->dev, "Could not register input device\n"); - goto err_free_irq; + return error; } /* let platform decide if keypad is a wakeup source or not */ @@ -444,30 +454,6 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) 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 @@ -518,7 +504,6 @@ static struct platform_driver tc3589x_keypad_driver = { .pm = &tc3589x_keypad_dev_pm_ops, }, .probe = tc3589x_keypad_probe, - .remove = tc3589x_keypad_remove, }; module_platform_driver(tc3589x_keypad_driver); diff --git a/kernel/drivers/input/keyboard/tca8418_keypad.c b/kernel/drivers/input/keyboard/tca8418_keypad.c index 4e491c176..900229869 100644 --- a/kernel/drivers/input/keyboard/tca8418_keypad.c +++ b/kernel/drivers/input/keyboard/tca8418_keypad.c @@ -404,7 +404,6 @@ MODULE_ALIAS("i2c:tca8418"); 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, diff --git a/kernel/drivers/input/keyboard/tegra-kbc.c b/kernel/drivers/input/keyboard/tegra-kbc.c index f97c73bd1..acc5394af 100644 --- a/kernel/drivers/input/keyboard/tegra-kbc.c +++ b/kernel/drivers/input/keyboard/tegra-kbc.c @@ -517,7 +517,8 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) kbc->use_ghost_filter = true; - if (of_find_property(np, "nvidia,wakeup-source", NULL)) + if (of_property_read_bool(np, "wakeup-source") || + of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */ kbc->wakeup = true; if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { @@ -705,7 +706,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) 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); + IRQF_TRIGGER_HIGH, pdev->name, kbc); if (err) { dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); return err; |