diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/gpio_decoder.c | 137 | ||||
-rw-r--r-- | drivers/input/misc/max77693-haptic.c | 4 | ||||
-rw-r--r-- | drivers/input/misc/tps65218-pwrbutton.c | 92 |
5 files changed, 219 insertions, 31 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index efb0ca871327..7ffb614ce566 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -292,6 +292,18 @@ config INPUT_GPIO_TILT_POLLED To compile this driver as a module, choose M here: the module will be called gpio_tilt_polled. +config INPUT_GPIO_DECODER + tristate "Polled GPIO Decoder Input driver" + depends on GPIOLIB || COMPILE_TEST + select INPUT_POLLDEV + help + Say Y here if you want driver to read status of multiple GPIO + lines and report the encoded value as an absolute integer to + input subsystem. + + To compile this driver as a module, choose M here: the module + will be called gpio_decoder. + config INPUT_IXP4XX_BEEPER tristate "IXP4XX Beeper support" depends on ARCH_IXP4XX @@ -454,10 +466,10 @@ config INPUT_RETU_PWRBUTTON config INPUT_TPS65218_PWRBUTTON tristate "TPS65218 Power button driver" - depends on MFD_TPS65218 + depends on (MFD_TPS65217 || MFD_TPS65218) help Say Y here if you want to enable power buttong reporting for - the TPS65218 Power Management IC device. + TPS65217 and TPS65218 Power Management IC devices. To compile this driver as a module, choose M here. The module will be called tps65218-pwrbutton. diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6a1e5e20fc1c..0b6d025f0487 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o +obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o diff --git a/drivers/input/misc/gpio_decoder.c b/drivers/input/misc/gpio_decoder.c new file mode 100644 index 000000000000..ca7e0bacb2d8 --- /dev/null +++ b/drivers/input/misc/gpio_decoder.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * A generic driver to read multiple gpio lines and translate the + * encoded numeric value into an input event. + */ + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +struct gpio_decoder { + struct input_polled_dev *poll_dev; + struct gpio_descs *input_gpios; + struct device *dev; + u32 axis; + u32 last_stable; +}; + +static int gpio_decoder_get_gpios_state(struct gpio_decoder *decoder) +{ + struct gpio_descs *gpios = decoder->input_gpios; + unsigned int ret = 0; + int i, val; + + for (i = 0; i < gpios->ndescs; i++) { + val = gpiod_get_value_cansleep(gpios->desc[i]); + if (val < 0) { + dev_err(decoder->dev, + "Error reading gpio %d: %d\n", + desc_to_gpio(gpios->desc[i]), val); + return val; + } + + val = !!val; + ret = (ret << 1) | val; + } + + return ret; +} + +static void gpio_decoder_poll_gpios(struct input_polled_dev *poll_dev) +{ + struct gpio_decoder *decoder = poll_dev->private; + int state; + + state = gpio_decoder_get_gpios_state(decoder); + if (state >= 0 && state != decoder->last_stable) { + input_report_abs(poll_dev->input, decoder->axis, state); + input_sync(poll_dev->input); + decoder->last_stable = state; + } +} + +static int gpio_decoder_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_decoder *decoder; + struct input_polled_dev *poll_dev; + u32 max; + int err; + + decoder = devm_kzalloc(dev, sizeof(struct gpio_decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + device_property_read_u32(dev, "linux,axis", &decoder->axis); + decoder->input_gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); + if (IS_ERR(decoder->input_gpios)) { + dev_err(dev, "unable to acquire input gpios\n"); + return PTR_ERR(decoder->input_gpios); + } + if (decoder->input_gpios->ndescs < 2) { + dev_err(dev, "not enough gpios found\n"); + return -EINVAL; + } + + if (device_property_read_u32(dev, "decoder-max-value", &max)) + max = (1U << decoder->input_gpios->ndescs) - 1; + + decoder->dev = dev; + poll_dev = devm_input_allocate_polled_device(decoder->dev); + if (!poll_dev) + return -ENOMEM; + + poll_dev->private = decoder; + poll_dev->poll = gpio_decoder_poll_gpios; + decoder->poll_dev = poll_dev; + + poll_dev->input->name = pdev->name; + poll_dev->input->id.bustype = BUS_HOST; + input_set_abs_params(poll_dev->input, decoder->axis, 0, max, 0, 0); + + err = input_register_polled_device(poll_dev); + if (err) { + dev_err(dev, "failed to register polled device\n"); + return err; + } + platform_set_drvdata(pdev, decoder); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id gpio_decoder_of_match[] = { + { .compatible = "gpio-decoder", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_decoder_of_match); +#endif + +static struct platform_driver gpio_decoder_driver = { + .probe = gpio_decoder_probe, + .driver = { + .name = "gpio-decoder", + .of_match_table = of_match_ptr(gpio_decoder_of_match), + } +}; +module_platform_driver(gpio_decoder_driver); + +MODULE_DESCRIPTION("GPIO decoder input driver"); +MODULE_AUTHOR("Vignesh R <vigneshr@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 29ddeb7be84b..46b0f48fbf49 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -3,7 +3,7 @@ * * Copyright (C) 2014,2015 Samsung Electronics * Jaewon Kim <jaewon02.kim@samsung.com> - * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * Krzysztof Kozlowski <krzk@kernel.org> * * This program is not provided / owned by Maxim Integrated Products. * @@ -426,7 +426,7 @@ static struct platform_driver max77693_haptic_driver = { module_platform_driver(max77693_haptic_driver); MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); -MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver"); MODULE_ALIAS("platform:max77693-haptic"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index a39b62651a4b..3273217ce80c 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -1,8 +1,9 @@ /* - * Texas Instruments' TPS65218 Power Button Input Driver + * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver * * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * Author: Felipe Balbi <balbi@ti.com> + * Author: Marcin Niestroj <m.niestroj@grinn-global.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 @@ -18,31 +19,61 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/kernel.h> +#include <linux/mfd/tps65217.h> #include <linux/mfd/tps65218.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.h> -struct tps65218_pwrbutton { +struct tps6521x_data { + unsigned int reg_status; + unsigned int pb_mask; + const char *name; +}; + +static const struct tps6521x_data tps65217_data = { + .reg_status = TPS65217_REG_STATUS, + .pb_mask = TPS65217_STATUS_PB, + .name = "tps65217_pwrbutton", +}; + +static const struct tps6521x_data tps65218_data = { + .reg_status = TPS65218_REG_STATUS, + .pb_mask = TPS65218_STATUS_PB_STATE, + .name = "tps65218_pwrbutton", +}; + +struct tps6521x_pwrbutton { struct device *dev; - struct tps65218 *tps; + struct regmap *regmap; struct input_dev *idev; + const struct tps6521x_data *data; + char phys[32]; +}; + +static const struct of_device_id of_tps6521x_pb_match[] = { + { .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data }, + { .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data }, + { }, }; +MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match); -static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr) +static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr) { - struct tps65218_pwrbutton *pwr = _pwr; + struct tps6521x_pwrbutton *pwr = _pwr; + const struct tps6521x_data *tps_data = pwr->data; unsigned int reg; int error; - error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®); + error = regmap_read(pwr->regmap, tps_data->reg_status, ®); if (error) { dev_err(pwr->dev, "can't read register: %d\n", error); goto out; } - if (reg & TPS65218_STATUS_PB_STATE) { + if (reg & tps_data->pb_mask) { input_report_key(pwr->idev, KEY_POWER, 1); pm_wakeup_event(pwr->dev, 0); } else { @@ -55,42 +86,55 @@ out: return IRQ_HANDLED; } -static int tps65218_pwron_probe(struct platform_device *pdev) +static int tps6521x_pb_probe(struct platform_device *pdev) { - struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; - struct tps65218_pwrbutton *pwr; + struct tps6521x_pwrbutton *pwr; struct input_dev *idev; + const struct of_device_id *match; int error; int irq; + match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node); + if (!match) + return -ENXIO; + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); if (!pwr) return -ENOMEM; + pwr->data = match->data; + idev = devm_input_allocate_device(dev); if (!idev) return -ENOMEM; - idev->name = "tps65218_pwrbutton"; - idev->phys = "tps65218_pwrbutton/input0"; + idev->name = pwr->data->name; + snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", + pwr->data->name); + idev->phys = pwr->phys; idev->dev.parent = dev; idev->id.bustype = BUS_I2C; input_set_capability(idev, EV_KEY, KEY_POWER); - pwr->tps = tps; + pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL); pwr->dev = dev; pwr->idev = idev; platform_set_drvdata(pdev, pwr); device_init_wakeup(dev, true); irq = platform_get_irq(pdev, 0); - error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq, + if (irq < 0) { + dev_err(dev, "No IRQ resource!\n"); + return -EINVAL; + } + + error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "tps65218-pwrbutton", pwr); + pwr->data->name, pwr); if (error) { dev_err(dev, "failed to request IRQ #%d: %d\n", irq, error); @@ -106,21 +150,15 @@ static int tps65218_pwron_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id of_tps65218_pwr_match[] = { - { .compatible = "ti,tps65218-pwrbutton" }, - { }, -}; -MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match); - -static struct platform_driver tps65218_pwron_driver = { - .probe = tps65218_pwron_probe, +static struct platform_driver tps6521x_pb_driver = { + .probe = tps6521x_pb_probe, .driver = { - .name = "tps65218_pwrbutton", - .of_match_table = of_tps65218_pwr_match, + .name = "tps6521x_pwrbutton", + .of_match_table = of_tps6521x_pb_match, }, }; -module_platform_driver(tps65218_pwron_driver); +module_platform_driver(tps6521x_pb_driver); -MODULE_DESCRIPTION("TPS65218 Power Button"); +MODULE_DESCRIPTION("TPS6521X Power Button"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); |